git_policy_cli/policy/policy_content.rs
1//! What:
2//! What one lifecycle offers the content policies,
3//! and the candidates it prepared
4//! for them,
5//! read once and shared by every content policy of the invocation.
6//! Why:
7//! A forwarded command other than `git add` has no candidates,
8//! so its content
9//! policies find nothing.
10//! `git add` and the direct commands have candidates,
11//! which
12//! are prepared the first time a content policy reads them:
13//! an invocation whose
14//! content policies are all off or escaped starts no Git process for them.
15//! A failure
16//! to prepare is remembered,
17//! so it is reported once and never retried.
18//!
19//! In TS you'd write (pseudocode):
20//! ```ts
21//! // const version = await content.version(lifecycle, facts); for (const c of version.candidates) await content.bytes(c);
22//! ```
23
24/// Import the layer's failure type and its causes.
25use crate::candidate_error::{CandidateError, CandidateFailure};
26/// Import the lookup of content that a parent commit's tree already holds.
27use crate::candidate_known_content::{ContentBasis, KnownContent, find_known_content};
28/// Import the candidate's mode,
29/// which a correction keeps.
30use crate::candidate_object::CandidateMode;
31/// Import what a lifecycle asks for and what preparing it yields.
32use crate::candidate_prediction::{CandidateRequest, PreparedCandidates};
33/// Import the store the overlay reads through.
34use crate::candidate_store::CandidateStore;
35/// Import the candidate and the immutable version type.
36use crate::candidate_version::{Candidate, CandidateVersion};
37/// Import the change a converged fix installs,
38/// which also records a corrected file.
39use crate::direct_fix_install::InstallChange;
40/// Import the outcome a check returns.
41use crate::policy_engine::PolicyOutcome;
42/// Import the facts interface that prepares candidates.
43use crate::repository_facts::RepositoryFacts;
44/// Import the scanner adapter.
45use crate::scanner_adapter::CandidateScanner;
46/// Import the code of each candidate failure.
47use crate::scanner_failure_code::candidate_failure_code;
48/// Import the scan pass,
49/// its byte reader and its failure.
50use crate::scanner_run::{CandidateBytes, ScanRunError, scan_version_skipping};
51/// The scanner library's per-candidate result.
52use forbidden_strings::CandidateScan;
53/// What:
54/// `BTreeMap<K, V>` is a map kept sorted by key (sibling `HashMap` is unordered).
55/// Why:
56/// Corrected files are keyed by pathname bytes,
57/// and their sorted order is Git's
58/// byte order,
59/// the order a fix summary lists them in.
60///
61/// In TS you'd write (pseudocode):
62/// ```ts
63/// // Map<string, Uint8Array>, iterated in sorted key order.
64/// ```
65use std::collections::{BTreeMap, HashSet};
66/// `Path` is a borrowed filesystem path.
67use std::path::Path;
68/// `Rc<T>` is a shared,
69/// read-only handle;
70/// versions and bytes are shared,
71/// not copied.
72use std::rc::Rc;
73
74/// What:
75/// What the current lifecycle offers a content policy.
76/// An `enum` is a closed set
77/// of named alternatives;
78/// `Requested` carries what to prepare.
79/// `#[derive(...)]`
80/// generates cloning,
81/// debug printing and `==`.
82/// Why:
83/// "There are no files to check" and "there are files,
84/// prepared on demand" must
85/// never be confused:
86/// the first is clean,
87/// the second must be read.
88///
89/// In TS you'd write (pseudocode):
90/// ```ts
91/// type LifecycleContent = { kind: 'none' } | { kind: 'requested'; request: CandidateRequest };
92/// ```
93#[derive(Clone, Debug, Eq, PartialEq)]
94pub enum LifecycleContent {
95 /// The lifecycle has no candidate content,
96 /// as for a forwarded command other than `git add`.
97 None,
98 /// The lifecycle has candidates;
99 /// they are prepared when a content policy first reads them.
100 Requested(
101 /// What to prepare.
102 CandidateRequest,
103 ),
104}
105
106/// What:
107/// The candidates of one invocation once prepared,
108/// or the remembered failure.
109/// `Option<Result<..>>` is "not yet asked",
110/// "prepared" or "failed".
111/// Why:
112/// Every content policy of the invocation reads the same version through one store.
113///
114/// In TS you'd write (pseudocode):
115/// ```ts
116/// class ContentState { #prepared?: PreparedCandidates | CandidateError }
117/// ```
118pub struct ContentState {
119 /// The prepared candidates,
120 /// or the failure of the one attempt to prepare them.
121 prepared: Option<Result<PreparedCandidates, CandidateError>>,
122 /// Each corrected file by pathname:
123 /// its mode,
124 /// original bytes and corrected bytes.
125 /// Every other file keeps its object's bytes.
126 corrected: BTreeMap<Vec<u8>, InstallChange>,
127 /// The corrections policies proposed in the current pass,
128 /// in proposal order.
129 proposals: Vec<Correction>,
130}
131
132/// What:
133/// One proposed full-content correction:
134/// the file,
135/// the bytes it held when the policy
136/// read it,
137/// and the bytes it should hold.
138/// `#[derive(...)]` generates cloning,
139/// debug
140/// printing and `==`.
141/// Why:
142/// A correction replaces a file's whole content,
143/// so applying it needs no patch
144/// format;
145/// the bytes it replaces are kept so the original of a file is known
146/// without reading it again.
147///
148/// In TS you'd write (pseudocode):
149/// ```ts
150/// type Correction = { path: Uint8Array; mode: CandidateMode; before: Uint8Array; after: Uint8Array };
151/// ```
152#[derive(Clone, Debug, Eq, PartialEq)]
153pub struct Correction {
154 /// The pathname,
155 /// relative to the repository's top level,
156 /// as Git's raw bytes.
157 pub path: Vec<u8>,
158 /// The file's mode,
159 /// which the correction keeps.
160 pub mode: CandidateMode,
161 /// The bytes the file held when the policy read it.
162 pub before: Rc<[u8]>,
163 /// The bytes the file should hold.
164 pub after: Rc<[u8]>,
165}
166
167/// What:
168/// The prepared store with the corrections laid over it,
169/// as a scan's byte reader.
170/// `'a` names how long both borrows last.
171/// Why:
172/// A later pass of a direct fix scans the corrected bytes,
173/// not the objects.
174///
175/// In TS you'd write (pseudocode):
176/// ```ts
177/// const reader: CandidateBytes = { bytes: (c) => corrected.get(c.path) ?? store.bytes(c) };
178/// ```
179struct OverlaidStore<'a> {
180 /// The store bound to the private index.
181 store: &'a mut CandidateStore,
182 /// The corrected files,
183 /// by pathname.
184 corrected: &'a BTreeMap<Vec<u8>, InstallChange>,
185}
186
187/// What:
188/// `impl CandidateBytes for OverlaidStore<'_>` reads corrected bytes first.
189/// Why:
190/// The one byte rule every reader of a direct fix shares.
191///
192/// In TS you'd write (pseudocode):
193/// ```ts
194/// class OverlaidStore implements CandidateBytes {}
195/// ```
196impl CandidateBytes for OverlaidStore<'_> {
197 /// What:
198 /// The corrected bytes of the candidate's file,
199 /// or its object's bytes.
200 /// Why:
201 /// See `overlaid_bytes`.
202 ///
203 /// In TS you'd write (pseudocode):
204 /// ```ts
205 /// bytes(candidate) { return overlaidBytes(this.store, this.corrected, candidate); }
206 /// ```
207 fn candidate_bytes(&mut self, candidate: &Candidate) -> Result<Rc<[u8]>, CandidateError> {
208 return overlaid_bytes(self.store, self.corrected, candidate);
209 }
210}
211
212/// What:
213/// The corrected bytes of a candidate's file when a correction gave it some,
214/// else
215/// the bytes of its object through the store.
216/// Why:
217/// Every policy of a later pass must read what the earlier passes made of the file.
218///
219/// In TS you'd write (pseudocode):
220/// ```ts
221/// const overlaidBytes = (store, corrected, candidate) => corrected.get(candidate.path) ?? store.bytes(candidate);
222/// ```
223fn overlaid_bytes(
224 store: &mut CandidateStore,
225 corrected: &BTreeMap<Vec<u8>, InstallChange>,
226 candidate: &Candidate,
227) -> Result<Rc<[u8]>, CandidateError> {
228 // `if let Some(found) = ...get(..)` runs only when the file was corrected.
229 if let Some(found) = corrected.get(&candidate.path) {
230 return Ok(Rc::clone(&found.replacement));
231 }
232 return store.bytes(candidate);
233}
234
235/// What:
236/// The outcome of a content policy whose candidates could not be prepared or read.
237/// `&CandidateError` borrows the failure;
238/// its code follows its cause.
239/// Why:
240/// Every cause but a sequencing defect is unreadable content;
241/// the message is the
242/// layer's own,
243/// which never contains a candidate's pathname.
244///
245/// In TS you'd write (pseudocode):
246/// ```ts
247/// const candidateFailure = (error: CandidateError): PolicyOutcome => ({ kind: 'failed', code: candidateFailureCode(error.failure), message: error.message });
248/// ```
249pub fn candidate_failure(error: &CandidateError) -> PolicyOutcome {
250 return PolicyOutcome::Failed {
251 code: candidate_failure_code(error.failure),
252 // `.clone()` copies the message the remembered failure keeps.
253 message: error.message.clone(),
254 // `None`: the layer's failures name no candidate.
255 path: None,
256 };
257}
258
259/// What:
260/// `impl ContentState { ... }` attaches the constructor and the two reads.
261/// Why:
262/// Preparation and reading stay behind one owner,
263/// so no policy can read bytes of a
264/// version it did not get from here.
265///
266/// In TS you'd write (pseudocode):
267/// ```ts
268/// class ContentState { version(lifecycle, facts) {} bytes(candidate) {} }
269/// ```
270impl ContentState {
271 /// What:
272 /// A state that has prepared nothing.
273 /// Why:
274 /// Preparation waits until a content policy reads.
275 ///
276 /// In TS you'd write (pseudocode):
277 /// ```ts
278 /// constructor() {}
279 /// ```
280 pub fn new() -> ContentState {
281 // `None` is the "absent" variant: nothing has been asked; nothing is corrected yet.
282 return ContentState {
283 prepared: None,
284 corrected: BTreeMap::new(),
285 proposals: Vec::new(),
286 };
287 }
288
289 /// What:
290 /// The candidate version,
291 /// prepared on first use;
292 /// `Ok(None)` when the lifecycle
293 /// has no candidates.
294 /// `<F: RepositoryFacts>` accepts any facts provider;
295 /// `Err(error)` is the candidate layer's failure,
296 /// remembered for later calls.
297 /// Why:
298 /// A direct command prepares its scope before any policy runs,
299 /// as the installed
300 /// wrapper does,
301 /// and reports a failure as a lifecycle failure of its own.
302 ///
303 /// In TS you'd write (pseudocode):
304 /// ```ts
305 /// async prepare(lifecycle: LifecycleContent, facts: RepositoryFacts): Promise<CandidateVersion | undefined>; // throws CandidateError
306 /// ```
307 pub fn prepare<F: RepositoryFacts>(
308 &mut self,
309 lifecycle: &LifecycleContent,
310 facts: &mut F,
311 ) -> Result<Option<Rc<CandidateVersion>>, CandidateError> {
312 let LifecycleContent::Requested(request) = lifecycle else {
313 return Ok(None);
314 };
315 if self.prepared.is_none() {
316 // `Some(...)` stores the one attempt's result, success or failure.
317 self.prepared = Some(facts.candidates(request));
318 }
319 // `match` on the remembered attempt; `&self.prepared` borrows it read-only.
320 match &self.prepared {
321 Some(Ok(prepared)) => return Ok(Some(Rc::clone(&prepared.version))),
322 // `.clone()` copies the remembered failure for this caller.
323 Some(Err(error)) => return Err(error.clone()),
324 None => return Err(not_prepared_error()),
325 }
326 }
327
328 /// What:
329 /// The candidate version for a content policy:
330 /// as `prepare`,
331 /// with a failure
332 /// turned into the outcome the policy returns as it is.
333 /// Why:
334 /// The first content policy that reads pays for the preparation;
335 /// later ones
336 /// share it,
337 /// and a failure is reported the same way to each.
338 ///
339 /// In TS you'd write (pseudocode):
340 /// ```ts
341 /// async version(lifecycle: LifecycleContent, facts: RepositoryFacts): Promise<CandidateVersion | undefined>; // throws PolicyOutcome
342 /// ```
343 pub fn version<F: RepositoryFacts>(
344 &mut self,
345 lifecycle: &LifecycleContent,
346 facts: &mut F,
347 ) -> Result<Option<Rc<CandidateVersion>>, PolicyOutcome> {
348 match self.prepare(lifecycle, facts) {
349 Ok(found) => return Ok(found),
350 Err(error) => return Err(candidate_failure(&error)),
351 }
352 }
353
354 /// What:
355 /// The bytes of a candidate of the prepared version,
356 /// with corrections applied.
357 /// Why:
358 /// Bytes come from the store bound to the private index,
359 /// never the live worktree,
360 /// unless a correction of this invocation replaced them.
361 ///
362 /// In TS you'd write (pseudocode):
363 /// ```ts
364 /// async bytes(candidate: Candidate): Promise<Uint8Array>;
365 /// ```
366 pub fn bytes(&mut self, candidate: &Candidate) -> Result<Rc<[u8]>, PolicyOutcome> {
367 // `&mut self.prepared` lends the remembered attempt for reading through its store.
368 match &mut self.prepared {
369 Some(Ok(prepared)) => {
370 match overlaid_bytes(&mut prepared.store, &self.corrected, candidate) {
371 Ok(read) => return Ok(read),
372 Err(error) => return Err(candidate_failure(&error)),
373 }
374 }
375 Some(Err(_)) | None => return Err(candidate_failure(¬_prepared_error())),
376 }
377 }
378
379 /// What:
380 /// The given candidate pathnames whose `text` Git attribute is unset,
381 /// read from the state the candidates were listed from.
382 /// `Err(outcome)` is the policy outcome of a lookup that failed.
383 /// Why:
384 /// A file that declares `-text` keeps its exact bytes.
385 /// Only the paths a policy would change are asked about,
386 /// so a commit that needs no change starts no attribute query.
387 ///
388 /// In TS you'd write (pseudocode):
389 /// ```ts
390 /// async textUnset(paths: Uint8Array[]): Promise<Set<string>>; // throws PolicyOutcome
391 /// ```
392 pub fn text_unset(&self, paths: &[&[u8]]) -> Result<HashSet<Vec<u8>>, PolicyOutcome> {
393 // `&self.prepared` borrows the remembered attempt read-only.
394 match &self.prepared {
395 Some(Ok(prepared)) => match prepared.attributes.text_unset(paths) {
396 Ok(unset) => return Ok(unset),
397 Err(error) => return Err(candidate_failure(&error)),
398 },
399 Some(Err(_)) | None => return Err(candidate_failure(¬_prepared_error())),
400 }
401 }
402
403 /// What:
404 /// The real index the prepared candidates were copied from,
405 /// once prepared.
406 /// Why:
407 /// A direct fix proves that installing its corrections left that index unchanged.
408 ///
409 /// In TS you'd write (pseudocode):
410 /// ```ts
411 /// get realIndexPath(): string | undefined;
412 /// ```
413 pub fn real_index(&self) -> Option<&Path> {
414 // `match &self.prepared` borrows the remembered attempt read-only.
415 match &self.prepared {
416 Some(Ok(prepared)) => return Some(prepared.real_index.as_path()),
417 Some(Err(_)) | None => return None,
418 }
419 }
420}
421
422/// What:
423/// `impl ContentState { ... }` continued:
424/// corrections of a fixing lifecycle.
425/// Why:
426/// A policy proposes,
427/// the fix loop applies after the pass,
428/// and later passes read
429/// the corrected bytes;
430/// nothing reaches the worktree until the fix installs them.
431///
432/// In TS you'd write (pseudocode):
433/// ```ts
434/// class ContentState { propose(c) {} takeProposals() {} apply(cs) {} }
435/// ```
436impl ContentState {
437 /// What:
438 /// Record one correction a policy proposes in the current pass.
439 /// Why:
440 /// Corrections wait until the pass ends,
441 /// so every policy of the pass reads the
442 /// same bytes.
443 ///
444 /// In TS you'd write (pseudocode):
445 /// ```ts
446 /// propose(correction: Correction): void;
447 /// ```
448 pub fn propose(&mut self, correction: Correction) {
449 self.proposals.push(correction);
450 }
451
452 /// What:
453 /// Hand over the current pass's proposals and forget them.
454 /// `std::mem::take` moves the list out and leaves an empty one behind.
455 /// Why:
456 /// Each pass starts with no proposals,
457 /// and a proposal is applied at most once.
458 ///
459 /// In TS you'd write (pseudocode):
460 /// ```ts
461 /// takeProposals(): Correction[];
462 /// ```
463 pub fn take_proposals(&mut self) -> Vec<Correction> {
464 return std::mem::take(&mut self.proposals);
465 }
466
467 /// What:
468 /// Apply corrections in order.
469 /// A file whose correction restores its original
470 /// bytes is no longer corrected.
471 /// Why:
472 /// The corrected map then equals another state's map exactly when both states
473 /// hold the same bytes in every file,
474 /// which the fix loop's cycle check needs.
475 /// A
476 /// file's original is the bytes its first correction replaced;
477 /// a file restored
478 /// and corrected again replaces those same bytes.
479 ///
480 /// In TS you'd write (pseudocode):
481 /// ```ts
482 /// apply(corrections: Correction[]): void;
483 /// ```
484 pub fn apply(&mut self, corrections: Vec<Correction>) {
485 for correction in corrections {
486 let original: Rc<[u8]> = match self.corrected.get(&correction.path) {
487 Some(file) => Rc::clone(&file.original),
488 None => correction.before,
489 };
490 if *original == *correction.after {
491 self.corrected.remove(&correction.path);
492 } else {
493 self.corrected.insert(
494 correction.path.clone(),
495 InstallChange {
496 path: correction.path,
497 mode: correction.mode,
498 original,
499 replacement: correction.after,
500 },
501 );
502 }
503 }
504 }
505
506 /// What:
507 /// A copy of every corrected file,
508 /// by pathname.
509 /// Copies share the bytes.
510 /// Why:
511 /// The fix loop keeps one per changed state to recognize a repeated state.
512 ///
513 /// In TS you'd write (pseudocode):
514 /// ```ts
515 /// correctedState(): Map<string, InstallChange>;
516 /// ```
517 pub fn corrected_state(&self) -> BTreeMap<Vec<u8>, InstallChange> {
518 return self.corrected.clone();
519 }
520
521 /// What:
522 /// Every corrected file,
523 /// in Git's byte order of pathnames.
524 /// Why:
525 /// These are the changes a converged fix installs,
526 /// and the order its summary
527 /// lists them in.
528 ///
529 /// In TS you'd write (pseudocode):
530 /// ```ts
531 /// installChanges(): InstallChange[];
532 /// ```
533 pub fn install_changes(&self) -> Vec<InstallChange> {
534 // `.values().cloned().collect()` copies each change in key order into a list.
535 return self.corrected.values().cloned().collect();
536 }
537}
538
539/// What:
540/// `impl ContentState { ... }` continued:
541/// the scan of every scannable candidate.
542/// Why:
543/// The scan pass reads bytes through the store the candidates were prepared in,
544/// with this invocation's corrections laid over it.
545///
546/// In TS you'd write (pseudocode):
547/// ```ts
548/// class ContentState { scan(scanner, rulesPath) {} }
549/// ```
550impl ContentState {
551 /// What:
552 /// Scan the prepared version through `scanner`.
553 /// `&[Vec<u8>]` is the rules
554 /// file's own pathname,
555 /// which is not scanned,
556 /// or nothing.
557 /// Why:
558 /// `scan_version` is the candidate layer's one scan pass;
559 /// this lends it the store
560 /// and the version together.
561 ///
562 /// In TS you'd write (pseudocode):
563 /// ```ts
564 /// async scan(scanner: CandidateScanner, rulesPath?: Buffer): Promise<CandidateScan[]>;
565 /// ```
566 pub fn scan(
567 &mut self,
568 scanner: &CandidateScanner,
569 rules_paths: &[Vec<u8>],
570 ) -> Result<Vec<CandidateScan>, ScanRunError> {
571 return self.scan_skipping(scanner, rules_paths, &KnownContent::default());
572 }
573
574 /// What:
575 /// `scan`,
576 /// except that the candidates in `known` are given to the scanner without bytes.
577 /// `&KnownContent` lists them by position in the prepared version.
578 /// Why:
579 /// A candidate whose content a parent already holds has its pathname checked and its
580 /// bytes left unread.
581 ///
582 /// In TS you'd write (pseudocode):
583 /// ```ts
584 /// async scanSkipping(scanner: CandidateScanner, rulesPath: Buffer | undefined, known: KnownContent): Promise<CandidateScan[]>;
585 /// ```
586 pub fn scan_skipping(
587 &mut self,
588 scanner: &CandidateScanner,
589 rules_paths: &[Vec<u8>],
590 known: &KnownContent,
591 ) -> Result<Vec<CandidateScan>, ScanRunError> {
592 match &mut self.prepared {
593 // The store is lent for writing and the version for reading: two separate fields.
594 Some(Ok(prepared)) => {
595 let mut reader: OverlaidStore<'_> = OverlaidStore {
596 store: &mut prepared.store,
597 corrected: &self.corrected,
598 };
599 return scan_version_skipping(
600 scanner,
601 &mut reader,
602 &prepared.version,
603 rules_paths,
604 known,
605 );
606 }
607 Some(Err(_)) | None => return Err(ScanRunError::Candidate(not_prepared_error())),
608 }
609 }
610
611 /// What:
612 /// The candidates of the prepared version whose content a parent commit's tree already holds,
613 /// as this lifecycle's request defines the parents.
614 /// `&[Vec<u8>]` is the rules files' own pathnames.
615 /// Why:
616 /// A file this invocation corrected holds new bytes whatever its object is,
617 /// so it is never known.
618 /// Nothing is listed before preparation or after a failed one.
619 ///
620 /// In TS you'd write (pseudocode):
621 /// ```ts
622 /// async knownContent(request: CandidateRequest, rulesPath?: Buffer): Promise<KnownContent>; // throws CandidateError
623 /// ```
624 pub fn known_content(
625 &mut self,
626 request: &CandidateRequest,
627 rules_paths: &[Vec<u8>],
628 ) -> Result<KnownContent, CandidateError> {
629 match &mut self.prepared {
630 Some(Ok(prepared)) => {
631 return find_known_content(
632 &mut prepared.store,
633 &prepared.version,
634 &ContentBasis::of(request),
635 rules_paths,
636 &self.corrected,
637 );
638 }
639 Some(Err(_)) | None => return Err(not_prepared_error()),
640 }
641 }
642}
643
644/// What: `impl ContentState { ... }` continued: the whole candidate state, for a policy
645/// that reads files beyond the candidates.
646/// Why: The dependent-version policy lists every tracked file of the private index and
647/// reads files that are not candidates, through the same store and the same
648/// corrections every other content policy reads through.
649///
650/// In TS you'd write (pseudocode):
651/// ```ts
652/// class ContentState { storeWithCorrections() {} }
653/// ```
654impl ContentState {
655 /// What: The store the candidates were prepared in, with this invocation's corrected
656 /// files; nothing before preparation or after a failed one. The two borrows are
657 /// of separate fields, so both can be held at once.
658 /// Why: A later pass of a direct fix must read what the earlier passes made of every
659 /// file, candidate or not.
660 ///
661 /// In TS you'd write (pseudocode):
662 /// ```ts
663 /// storeWithCorrections(): [CandidateStore, Map<string, InstallChange>] | undefined;
664 /// ```
665 pub fn store_with_corrections(
666 &mut self,
667 ) -> Option<(&mut CandidateStore, &BTreeMap<Vec<u8>, InstallChange>)> {
668 match &mut self.prepared {
669 Some(Ok(prepared)) => return Some((&mut prepared.store, &self.corrected)),
670 Some(Err(_)) | None => return None,
671 }
672 }
673}
674
675/// What: The failure of a read made before any version was prepared.
676/// Why: Only a defect in a calling policy can cause it. It is reported as a stale
677/// candidate, whose code is `policy-incomplete`: the policy's own sequencing
678/// failed, and the read must still not look clean.
679///
680/// In TS you'd write (pseudocode):
681/// ```ts
682/// const notPreparedError = () => new CandidateError('stale-candidate', '...');
683/// ```
684fn not_prepared_error() -> CandidateError {
685 return CandidateError::new(
686 CandidateFailure::StaleCandidate,
687 "cli-git read candidate content before the candidates were prepared; this is a defect in cli-git.",
688 );
689}
690
691/// What:
692/// `impl Default for ContentState` lets `ContentState::default()` build the empty state.
693/// Why:
694/// Clippy asks a type with a `new()` without arguments to provide `Default` too.
695///
696/// In TS you'd write (pseudocode):
697/// ```ts
698/// static default(): ContentState { return new ContentState(); }
699/// ```
700impl Default for ContentState {
701 /// What:
702 /// The state that has prepared nothing.
703 /// Why:
704 /// same as `new`.
705 ///
706 /// In TS you'd write (pseudocode):
707 /// ```ts
708 /// static default(): ContentState
709 /// ```
710 fn default() -> ContentState {
711 return ContentState::new();
712 }
713}
714
715/// Preparation,
716/// sharing and failure controls stay out of the release executable.
717#[cfg(test)]
718#[path = "policy_content_tests.rs"]
719mod tests;