Unified · Split
| @@ -7,119 +7,7 @@ pub(crate) mod types; · 6 unmodified lines | ||
| 7 | 7 | pub use types::{Item, Mapping, Outcome, Source, SourceRef}; |
| 8 | 8 | |
| 9 | 9 | /// |
| 10 | −pub mod validate { | |
| 11 | − use crate::match_group::{Outcome, Source}; | |
| 12 | − use bstr::BString; | |
| 13 | − use std::collections::BTreeMap; | |
| 14 | − | |
| 15 | − /// All possible issues found while validating matched mappings. | |
| 16 | − #[derive(Debug, PartialEq, Eq)] | |
| 17 | − pub enum Issue { | |
| 18 | − /// Multiple sources try to write the same destination. | |
| 19 | − /// | |
| 20 | − /// Note that this issue doesn't take into consideration that these sources might contain the same object behind a reference. | |
| 21 | − Conflict { | |
| 22 | − /// The unenforced full name of the reference to be written. | |
| 23 | − destination_full_ref_name: BString, | |
| 24 | − /// The list of sources that map to this destination. | |
| 25 | − sources: Vec<Source>, | |
| 26 | − /// The list of specs that caused the mapping conflict, each matching the respective one in `sources` to allow both | |
| 27 | − /// `sources` and `specs` to be zipped together. | |
| 28 | − specs: Vec<BString>, | |
| 29 | − }, | |
| 30 | − } | |
| 31 | − | |
| 32 | − impl std::fmt::Display for Issue { | |
| 33 | − fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 34 | − match self { | |
| 35 | − Issue::Conflict { | |
| 36 | − destination_full_ref_name, | |
| 37 | − sources, | |
| 38 | − specs, | |
| 39 | − } => { | |
| 40 | − write!( | |
| 41 | − f, | |
| 42 | − "Conflicting destination {destination_full_ref_name:?} would be written by {}", | |
| 43 | − sources | |
| 44 | − .iter() | |
| 45 | − .zip(specs.iter()) | |
| 46 | − .map(|(src, spec)| format!("{src} ({spec:?})")) | |
| 47 | − .collect::<Vec<_>>() | |
| 48 | − .join(", ") | |
| 49 | − ) | |
| 50 | − } | |
| 51 | − } | |
| 52 | − } | |
| 53 | − } | |
| 54 | − | |
| 55 | − /// All possible fixes corrected while validating matched mappings. | |
| 56 | − #[derive(Debug, PartialEq, Eq)] | |
| 57 | − pub enum Fix {} | |
| 58 | − | |
| 59 | − /// The error returned [outcome validation][Outcome::validated()]. | |
| 60 | − #[derive(Debug)] | |
| 61 | − pub struct Error { | |
| 62 | − /// All issues discovered during validation. | |
| 63 | − pub issues: Vec<Issue>, | |
| 64 | − } | |
| 65 | − | |
| 66 | − impl std::fmt::Display for Error { | |
| 67 | − fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 68 | − write!( | |
| 69 | − f, | |
| 70 | − "Found {} {} the refspec mapping to be used: \n\t{}", | |
| 71 | − self.issues.len(), | |
| 72 | − (self.issues.len() == 1) | |
| 73 | − .then(|| "issue that prevents") | |
| 74 | − .unwrap_or("issues that prevent"), | |
| 75 | − self.issues | |
| 76 | − .iter() | |
| 77 | − .map(|issue| issue.to_string()) | |
| 78 | − .collect::<Vec<_>>() | |
| 79 | − .join("\n\t") | |
| 80 | − ) | |
| 81 | − } | |
| 82 | − } | |
| 83 | − | |
| 84 | − impl std::error::Error for Error {} | |
| 85 | − | |
| 86 | − impl<'spec, 'item> Outcome<'spec, 'item> { | |
| 87 | − /// Validate all mappings or dissolve them into an error stating the discovered issues. | |
| 88 | − /// Return `(modified self, issues)` providing a fixed-up set of mappings in `self` with the fixed `issues` | |
| 89 | − /// provided as part of it. | |
| 90 | − /// Terminal issues are communicated using the [`Error`] type accordingly. | |
| 91 | − pub fn validated(self) -> Result<(Self, Vec<Fix>), Error> { | |
| 92 | − let fixed = Vec::new(); | |
| 93 | − let mut sources_by_destinations = BTreeMap::new(); | |
| 94 | − for (dst, (spec_index, src)) in self | |
| 95 | − .mappings | |
| 96 | − .iter() | |
| 97 | − .filter_map(|m| m.rhs.as_ref().map(|dst| (dst.as_ref(), (m.spec_index, &m.lhs)))) | |
| 98 | − { | |
| 99 | − let sources = sources_by_destinations.entry(dst).or_insert_with(Vec::new); | |
| 100 | − if !sources.iter().any(|(_, lhs)| lhs == &src) { | |
| 101 | − sources.push((spec_index, src)) | |
| 102 | − } | |
| 103 | − } | |
| 104 | − let mut issues = Vec::new(); | |
| 105 | − for (dst, conflicting_sources) in sources_by_destinations.into_iter().filter(|(_, v)| v.len() > 1) { | |
| 106 | − issues.push(Issue::Conflict { | |
| 107 | − destination_full_ref_name: dst.to_owned(), | |
| 108 | − specs: conflicting_sources | |
| 109 | − .iter() | |
| 110 | − .map(|(spec_idx, _)| self.group.specs[*spec_idx].to_bstring()) | |
| 111 | − .collect(), | |
| 112 | − sources: conflicting_sources.into_iter().map(|(_, src)| src.to_owned()).collect(), | |
| 113 | − }) | |
| 114 | − } | |
| 115 | − if !issues.is_empty() { | |
| 116 | − Err(Error { issues }) | |
| 117 | − } else { | |
| 118 | − Ok((self, fixed)) | |
| 119 | − } | |
| 120 | − } | |
| 121 | − } | |
| 122 | −} | |
| 10 | +pub mod validate; | |
| 123 | 11 | |
| 124 | 12 | /// Initialization |
| 125 | 13 | impl<'a> MatchGroup<'a> { |
| @@ -0,0 +1,111 @@ | ||
| 1 | +use crate::match_group::{Outcome, Source}; | |
| 2 | +use bstr::BString; | |
| 3 | +use std::collections::BTreeMap; | |
| 4 | + | |
| 5 | +/// All possible issues found while validating matched mappings. | |
| 6 | +#[derive(Debug, PartialEq, Eq)] | |
| 7 | +pub enum Issue { | |
| 8 | + /// Multiple sources try to write the same destination. | |
| 9 | + /// | |
| 10 | + /// Note that this issue doesn't take into consideration that these sources might contain the same object behind a reference. | |
| 11 | + Conflict { | |
| 12 | + /// The unenforced full name of the reference to be written. | |
| 13 | + destination_full_ref_name: BString, | |
| 14 | + /// The list of sources that map to this destination. | |
| 15 | + sources: Vec<Source>, | |
| 16 | + /// The list of specs that caused the mapping conflict, each matching the respective one in `sources` to allow both | |
| 17 | + /// `sources` and `specs` to be zipped together. | |
| 18 | + specs: Vec<BString>, | |
| 19 | + }, | |
| 20 | +} | |
| 21 | + | |
| 22 | +impl std::fmt::Display for Issue { | |
| 23 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 24 | + match self { | |
| 25 | + Issue::Conflict { | |
| 26 | + destination_full_ref_name, | |
| 27 | + sources, | |
| 28 | + specs, | |
| 29 | + } => { | |
| 30 | + write!( | |
| 31 | + f, | |
| 32 | + "Conflicting destination {destination_full_ref_name:?} would be written by {}", | |
| 33 | + sources | |
| 34 | + .iter() | |
| 35 | + .zip(specs.iter()) | |
| 36 | + .map(|(src, spec)| format!("{src} ({spec:?})")) | |
| 37 | + .collect::<Vec<_>>() | |
| 38 | + .join(", ") | |
| 39 | + ) | |
| 40 | + } | |
| 41 | + } | |
| 42 | + } | |
| 43 | +} | |
| 44 | + | |
| 45 | +/// All possible fixes corrected while validating matched mappings. | |
| 46 | +#[derive(Debug, PartialEq, Eq)] | |
| 47 | +pub enum Fix {} | |
| 48 | + | |
| 49 | +/// The error returned [outcome validation][Outcome::validated()]. | |
| 50 | +#[derive(Debug)] | |
| 51 | +pub struct Error { | |
| 52 | + /// All issues discovered during validation. | |
| 53 | + pub issues: Vec<Issue>, | |
| 54 | +} | |
| 55 | + | |
| 56 | +impl std::fmt::Display for Error { | |
| 57 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 58 | + write!( | |
| 59 | + f, | |
| 60 | + "Found {} {} the refspec mapping to be used: \n\t{}", | |
| 61 | + self.issues.len(), | |
| 62 | + (self.issues.len() == 1) | |
| 63 | + .then(|| "issue that prevents") | |
| 64 | + .unwrap_or("issues that prevent"), | |
| 65 | + self.issues | |
| 66 | + .iter() | |
| 67 | + .map(|issue| issue.to_string()) | |
| 68 | + .collect::<Vec<_>>() | |
| 69 | + .join("\n\t") | |
| 70 | + ) | |
| 71 | + } | |
| 72 | +} | |
| 73 | + | |
| 74 | +impl std::error::Error for Error {} | |
| 75 | + | |
| 76 | +impl<'spec, 'item> Outcome<'spec, 'item> { | |
| 77 | + /// Validate all mappings or dissolve them into an error stating the discovered issues. | |
| 78 | + /// Return `(modified self, issues)` providing a fixed-up set of mappings in `self` with the fixed `issues` | |
| 79 | + /// provided as part of it. | |
| 80 | + /// Terminal issues are communicated using the [`Error`] type accordingly. | |
| 81 | + pub fn validated(self) -> Result<(Self, Vec<Fix>), Error> { | |
| 82 | + let fixed = Vec::new(); | |
| 83 | + let mut sources_by_destinations = BTreeMap::new(); | |
| 84 | + for (dst, (spec_index, src)) in self | |
| 85 | + .mappings | |
| 86 | + .iter() | |
| 87 | + .filter_map(|m| m.rhs.as_ref().map(|dst| (dst.as_ref(), (m.spec_index, &m.lhs)))) | |
| 88 | + { | |
| 89 | + let sources = sources_by_destinations.entry(dst).or_insert_with(Vec::new); | |
| 90 | + if !sources.iter().any(|(_, lhs)| lhs == &src) { | |
| 91 | + sources.push((spec_index, src)) | |
| 92 | + } | |
| 93 | + } | |
| 94 | + let mut issues = Vec::new(); | |
| 95 | + for (dst, conflicting_sources) in sources_by_destinations.into_iter().filter(|(_, v)| v.len() > 1) { | |
| 96 | + issues.push(Issue::Conflict { | |
| 97 | + destination_full_ref_name: dst.to_owned(), | |
| 98 | + specs: conflicting_sources | |
| 99 | + .iter() | |
| 100 | + .map(|(spec_idx, _)| self.group.specs[*spec_idx].to_bstring()) | |
| 101 | + .collect(), | |
| 102 | + sources: conflicting_sources.into_iter().map(|(_, src)| src.to_owned()).collect(), | |
| 103 | + }) | |
| 104 | + } | |
| 105 | + if !issues.is_empty() { | |
| 106 | + Err(Error { issues }) | |
| 107 | + } else { | |
| 108 | + Ok((self, fixed)) | |
| 109 | + } | |
| 110 | + } | |
| 111 | +} | |