Skip to main content

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(&not_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(&not_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;