1use alloc::collections::{BTreeMap, BTreeSet};
15use core::{mem, slice};
16
17use semver::Version;
18use serde::Serialize;
19
20use crate::collection::{Index, label};
21use crate::digest::Digest;
22use crate::errors::{ProfileError, Result, TargetError};
23use crate::format::Format;
24use crate::gate::{self, Pattern, Reason};
25use crate::graph::{Configured, Graph, Load, Node};
26pub use crate::graph::{Enabler, Warning};
27pub use crate::merge::Driver;
28use crate::name::{FeatureName, ProfileName, ScaffoldName, SourceName};
29use crate::part::{Key, Scope, Shape, Slot, Written, display, overlap};
30use crate::path::RelPath;
31use crate::profile::{
32 FileSpec, MANIFEST, Manifest, MergeSpec, Meta, PAYLOAD, Policy, ScaffoldSpec,
33};
34pub use crate::settings::{Settings, Suggestion};
35use crate::source::{Cache, Oid, Reader, Source};
36use crate::survey::Entry;
37use crate::target::{Override, Target, from_toml};
38use crate::tree::Tree;
39pub use crate::vars::Question;
40use crate::vars::{self, Context, VarName, VarSpec};
41
42#[derive(Clone, Copy, Debug)]
44pub enum Refresh<'a> {
45 None,
47 All,
49 Only(&'a str),
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
55#[serde(rename_all = "lowercase")]
56pub enum Applied {
57 Current,
59 Changed,
62 Never,
64}
65
66#[derive(Debug)]
68pub struct Layer {
69 source: Source,
71 source_name: Option<SourceName>,
73 dir: String,
75 meta: Meta,
77 merge: MergeSpec,
79 vars: BTreeMap<VarName, VarSpec>,
81 declared: Vec<FeatureName>,
83 features: BTreeMap<FeatureName, BTreeSet<Enabler>>,
85 required_by: Vec<ProfileName>,
87 configured: bool,
89 rev: Option<Oid>,
91 manifest: Digest,
93 digest: Digest,
95 locked: Option<Digest>,
97 specs: Specs,
99 scaffolds: BTreeMap<ScaffoldName, Vec<Pattern>>,
101 files: BTreeMap<RelPath, LayerFile>,
103 written: Tree,
105 payload: Tree,
107 starters: Tree,
109}
110
111pub(crate) type Specs = (BTreeMap<RelPath, FileSpec>, BTreeMap<ScaffoldName, ScaffoldSpec>);
113
114#[derive(Clone, Debug)]
116pub struct LayerFile {
117 pub written: RelPath,
119 pub spec: FileSpec,
121 pub(crate) exists: Vec<Pattern>,
123 pub gated: Option<Reason>,
125}
126
127impl LayerFile {
128 pub fn exists(&self) -> impl Iterator<Item = &str> {
130 self.exists.iter().map(Pattern::as_str)
131 }
132}
133
134impl Layer {
135 #[must_use]
137 pub const fn source(&self) -> &Source {
138 &self.source
139 }
140
141 #[must_use]
143 pub const fn source_name(&self) -> Option<&SourceName> {
144 self.source_name.as_ref()
145 }
146
147 #[must_use]
149 pub const fn name(&self) -> &ProfileName {
150 &self.meta.name
151 }
152
153 #[must_use]
156 pub fn qualified(&self) -> String {
157 let name = &self.meta.name;
158 self.source_name
159 .as_ref()
160 .map_or_else(|| name.to_string(), |source| format!("{source}/{name}"))
161 }
162
163 #[must_use]
165 pub const fn meta(&self) -> &Meta {
166 &self.meta
167 }
168
169 #[must_use]
171 pub const fn merge(&self) -> &MergeSpec {
172 &self.merge
173 }
174
175 #[must_use]
177 pub const fn rev(&self) -> Option<&Oid> {
178 self.rev.as_ref()
179 }
180
181 #[must_use]
183 pub const fn digest(&self) -> Digest {
184 self.digest
185 }
186
187 #[must_use]
189 pub fn declared(&self) -> &[FeatureName] {
190 &self.declared
191 }
192
193 #[must_use]
195 pub const fn features(&self) -> &BTreeMap<FeatureName, BTreeSet<Enabler>> {
196 &self.features
197 }
198
199 #[must_use]
201 pub fn required_by(&self) -> &[ProfileName] {
202 &self.required_by
203 }
204
205 #[must_use]
207 pub const fn configured(&self) -> bool {
208 self.configured
209 }
210
211 #[must_use]
213 pub const fn files(&self) -> &BTreeMap<RelPath, LayerFile> {
214 &self.files
215 }
216
217 pub fn scaffolds(&self) -> impl Iterator<Item = (&ScaffoldName, impl Iterator<Item = &str>)> {
219 self.scaffolds.iter().map(|(name, unless)| (name, unless.iter().map(Pattern::as_str)))
220 }
221
222 #[must_use]
224 pub fn applied(&self) -> Applied {
225 match self.locked {
226 None => Applied::Never,
227 Some(locked) if locked == self.digest => Applied::Current,
228 Some(_) => Applied::Changed,
229 }
230 }
231
232 pub(crate) fn vars(&self) -> impl Iterator<Item = (&VarName, &VarSpec)> {
234 self.vars.iter()
235 }
236
237 pub(crate) const fn sentinels(&self) -> &BTreeMap<ScaffoldName, Vec<Pattern>> {
239 &self.scaffolds
240 }
241
242 pub(crate) fn label(&self, file: &str) -> String {
244 if self.dir.is_empty() {
245 label(&self.source, file)
246 } else {
247 label(&self.source, &format!("{}/{file}", self.dir))
248 }
249 }
250
251 pub(crate) fn take_specs(&mut self) -> Specs {
253 mem::take(&mut self.specs)
254 }
255
256 pub(crate) fn set_files(
258 &mut self, files: BTreeMap<RelPath, LayerFile>,
259 scaffolds: BTreeMap<ScaffoldName, Vec<Pattern>>,
260 ) {
261 self.files = files;
262 self.scaffolds = scaffolds;
263 }
264
265 pub(crate) const fn files_mut(&mut self) -> &mut BTreeMap<RelPath, LayerFile> {
267 &mut self.files
268 }
269
270 pub(crate) const fn written(&self) -> &Tree {
272 &self.written
273 }
274
275 pub(crate) fn set_payload(&mut self, payload: Tree, starters: Tree) {
277 let mut hasher = blake3::Hasher::new();
278 hasher.update(self.manifest.as_bytes());
279 hasher.update(payload.digest().as_bytes());
280 hasher.update(starters.digest().as_bytes());
281 for feature in self.features.keys() {
282 hasher.update(feature.as_str().as_bytes());
283 hasher.update(&[0]);
284 }
285 self.digest = Digest::new(hasher.finalize());
286 self.payload = payload;
287 self.starters = starters;
288 self.written = Tree::default();
289 }
290}
291
292#[derive(Clone, Debug)]
294pub(crate) struct Provided {
295 pub layer: usize,
297 pub policy: Policy,
299 pub validate: Format,
301 pub executable: bool,
303 pub portion: Option<Portion>,
305}
306
307#[derive(Clone, Debug)]
309pub(crate) struct Portion {
310 pub scope: Scope,
312 pub shape: Shape,
314 pub content: Vec<u8>,
316 pub payload: Written,
318 pub text: Vec<u8>,
320 pub starts: bool,
322 pub starter: Option<Vec<u8>>,
324}
325
326impl Portion {
327 pub(crate) fn splice(
332 &self, file: &[u8], content: &[u8], keys: &BTreeSet<Key>, label: &str,
333 ) -> Result<Vec<u8>> {
334 let keyed = matches!(self.shape, Shape::Keys(_));
335 if keyed && content == self.content && file.trim_ascii().is_empty() {
336 return Ok(self.text.clone());
337 }
338 self.shape.splice(file, content, keys, &self.payload, label)
339 }
340}
341
342#[derive(Debug)]
345pub struct Resolved {
346 layers: Vec<Layer>,
348 providers: BTreeMap<Slot, Provided>,
350 settings: Settings,
352 suggestions: Vec<Suggestion>,
354 answers: BTreeMap<VarName, String>,
356 warnings: Vec<Warning>,
358}
359
360impl Resolved {
361 #[must_use]
363 pub fn layers(&self) -> &[Layer] {
364 &self.layers
365 }
366
367 #[must_use]
369 pub fn layer(&self, name: &str) -> Option<&Layer> {
370 self.layers.iter().find(|layer| layer.meta.name == *name)
371 }
372
373 #[must_use]
375 pub const fn settings(&self) -> &Settings {
376 &self.settings
377 }
378
379 #[must_use]
381 pub fn suggestions(&self) -> &[Suggestion] {
382 &self.suggestions
383 }
384
385 #[must_use]
387 pub const fn answers(&self) -> &BTreeMap<VarName, String> {
388 &self.answers
389 }
390
391 #[must_use]
393 pub fn warnings(&self) -> &[Warning] {
394 &self.warnings
395 }
396
397 #[must_use]
399 pub fn provider(&self, entry: &Entry) -> Option<&ProfileName> {
400 Some(&self.layers.get(entry.want?.layer)?.meta.name)
401 }
402
403 #[must_use]
405 pub fn file(&self, entry: &Entry) -> Option<&LayerFile> {
406 self.layers.get(entry.want?.layer)?.files.get(&entry.path)
407 }
408
409 pub(crate) fn provided(&self) -> impl Iterator<Item = (&Slot, &Provided, &[u8])> {
411 self.providers
412 .iter()
413 .filter_map(|(slot, provided)| Some((slot, provided, self.bytes(slot, provided)?)))
414 }
415
416 pub(crate) fn payload(&self, entry: &Entry) -> Result<&[u8]> {
422 let slot = entry.slot();
423 let bytes = self.providers.get(&slot).and_then(|provided| self.bytes(&slot, provided));
424 bytes.ok_or_else(|| {
425 TargetError::NotManaged { path: entry.path.to_string(), managed: Vec::new() }.into()
426 })
427 }
428
429 fn bytes<'a>(&'a self, slot: &Slot, provided: &'a Provided) -> Option<&'a [u8]> {
431 match &provided.portion {
432 Some(portion) => Some(&portion.content),
433 None => self.layers.get(provided.layer)?.payload.get(&slot.path),
434 }
435 }
436
437 pub(crate) fn portion(&self, entry: &Entry) -> Option<&Portion> {
440 self.providers.get(&entry.slot())?.portion.as_ref()
441 }
442}
443
444pub fn resolve(target: &Target, cache: &Cache, refresh: Refresh<'_>) -> Result<Resolved> {
459 let moves = Moves::of(target, cache, refresh)?;
460 let (mut layers, warnings) = layers(target, cache, &moves)?;
461 let answers = vars::answers(&layers, target)?;
462 let context = Context::of(target, &layers);
463 vars::render_paths(&mut layers, &answers, &context)?;
464 gate::statics(&mut layers, &answers);
465 let config = target.config();
466 let mut providers = providers(&layers, &config.files)?;
467 vars::render(&mut layers, &answers, &context)?;
468 portions(&layers, &mut providers)?;
469 let settings = Settings::compose(&layers, &config.merge)?;
470 let suggestions = Settings::suggestions(&layers, &config.merge);
471 Ok(Resolved { layers, providers, settings, suggestions, answers, warnings })
472}
473
474enum Moves {
476 None,
478 All,
480 Only(Vec<Source>),
482}
483
484impl Moves {
485 fn of(target: &Target, cache: &Cache, refresh: Refresh<'_>) -> Result<Self> {
487 let name = match refresh {
488 Refresh::None => return Ok(Self::None),
489 Refresh::All => return Ok(Self::All),
490 Refresh::Only(name) => name,
491 };
492 let config = target.config();
493 if let Some(source) = config.sources.iter().find(|(named, _)| named.as_str() == name) {
494 return Ok(Self::Only(vec![source.1.clone()]));
495 }
496 let configured = config.layers.iter().find(|layer| layer.profile.profile == *name);
497 if let Some(source) = configured.and_then(|layer| config.sources.get(&layer.profile.source))
498 {
499 return Ok(Self::Only(vec![source.clone()]));
500 }
501 let (layers, _) = layers(target, cache, &Self::None)?;
502 if let Some(layer) = layers.iter().find(|layer| layer.meta.name == *name) {
503 return Ok(Self::Only(vec![layer.source.clone()]));
504 }
505 let layers = layers.iter().map(|layer| layer.meta.name.to_string());
506 let names = layers.chain(config.sources.keys().map(ToString::to_string)).collect();
507 Err(TargetError::NoSuchLayer { name: name.to_owned(), names }.into())
508 }
509
510 fn moves(&self, source: &Source) -> bool {
512 match self {
513 Self::None => false,
514 Self::All => true,
515 Self::Only(sources) => sources.contains(source),
516 }
517 }
518}
519
520fn layers(target: &Target, cache: &Cache, moves: &Moves) -> Result<(Vec<Layer>, Vec<Warning>)> {
522 let config = target.config();
523 let configured = config
524 .layers
525 .iter()
526 .map(|layer| {
527 let source = config.sources.get(&layer.profile.source).ok_or_else(|| {
528 let sources = config.sources.keys().cloned().collect();
529 let name = layer.profile.source.clone();
530 let naming = config.layers.iter().filter(|l| l.profile.source == name);
531 let layers = naming.map(|l| l.profile.clone()).collect();
532 TargetError::NoSuchSource { name, sources, layers }
533 })?;
534 Ok(Configured {
535 source,
536 name: &layer.profile.profile,
537 features: &layer.features,
538 defaults: layer.default_features,
539 })
540 })
541 .collect::<Result<Vec<_>>>()?;
542 let mut loader = Loader { target, cache, moves, opened: Vec::new() };
543 let Graph { nodes, warnings } = Graph::build(&configured, &mut loader)?;
544 let layers = nodes.into_iter().map(|node| loader.layer(node)).collect::<Result<_>>()?;
545 Ok((layers, warnings))
546}
547
548struct Loader<'a> {
550 target: &'a Target,
552 cache: &'a Cache,
554 moves: &'a Moves,
556 opened: Vec<Opened>,
558}
559
560struct Opened {
562 source: Source,
564 reader: Reader,
566 index: Index,
568}
569
570pub(crate) struct Loaded {
572 opened: usize,
574 dir: String,
576 manifest: Manifest,
578 digest: Digest,
580}
581
582impl Load for Loader<'_> {
583 type Profile = Loaded;
584
585 fn load(&mut self, source: &Source, name: &ProfileName) -> Result<Loaded> {
586 let opened = self.open(source)?;
587 let Some(Opened { reader, index, .. }) = self.opened.get(opened) else {
588 return Err(ProfileError::NoProfiles { location: source.to_string() }.into());
589 };
590 let dir = index.find(name, source)?.to_owned();
591 let path = if dir.is_empty() {
592 RelPath::new(MANIFEST)?
593 } else {
594 RelPath::new(&format!("{dir}/{MANIFEST}"))?
595 };
596 let read = reader.read("", slice::from_ref(&path))?;
597 let raw = read
598 .get(&path)
599 .ok_or_else(|| ProfileError::NoProfiles { location: source.to_string() })?;
600 let file = label(source, path.as_str());
601 let manifest: Manifest = from_toml(raw, &file)?;
602 if let Some(required) = &manifest.profile.devset
603 && !Version::parse(crate::VERSION).is_ok_and(|version| required.matches(&version))
604 {
605 let (profile, requires) = (manifest.profile.name, required.to_string());
606 return Err(ProfileError::Incompatible { profile, requires }.into());
607 }
608 manifest.check(&file)?;
609 Ok(Loaded { opened, dir, manifest, digest: Digest::of(raw) })
610 }
611
612 fn manifest(profile: &Loaded) -> &Manifest {
613 &profile.manifest
614 }
615}
616
617impl Loader<'_> {
618 fn open(&mut self, source: &Source) -> Result<usize> {
624 if let Some(at) = self.opened.iter().position(|opened| opened.source == *source) {
625 return Ok(at);
626 }
627 if let Some(twin) = self.opened.iter().find(|opened| opened.source.twin(source)) {
628 let (first, second) = (twin.source.to_string(), source.to_string());
629 return Err(ProfileError::Diverged { first, second }.into());
630 }
631 let pin = if self.moves.moves(source) { None } else { self.target.pinned(source) };
632 let reader = source.open(self.target.root(), pin, self.cache)?;
633 let index = Index::read(&reader, source)?;
634 if index.names().next().is_none() {
635 return Err(ProfileError::NoProfiles { location: source.to_string() }.into());
636 }
637 self.opened.push(Opened { source: source.clone(), reader, index });
638 Ok(self.opened.len().saturating_sub(1))
639 }
640
641 fn layer(&self, node: Node<Loaded>) -> Result<Layer> {
643 let Node { profile, name, source, features, required_by, configured, .. } = node;
644 let Loaded { opened, dir, manifest, digest } = profile;
645 let Some(Opened { reader, .. }) = self.opened.get(opened) else {
646 return Err(ProfileError::NoProfiles { location: source.to_string() }.into());
647 };
648 let paths: Vec<RelPath> = manifest.payload_paths().into_iter().collect();
649 let payload_dir =
650 if dir.is_empty() { PAYLOAD.to_owned() } else { format!("{dir}/{PAYLOAD}") };
651 let written = reader.read(&payload_dir, &paths)?;
652 if let Some(missing) = paths.into_iter().find(|path| written.get(path).is_none()) {
653 return Err(ProfileError::MissingPayload { profile: name, path: missing }.into());
654 }
655 let config = self.target.config();
656 let source_name = config
657 .sources
658 .iter()
659 .find(|(_, named)| **named == source)
660 .map(|(name, _)| name.clone());
661 let locked = self.target.locked(&source, &name);
662 let Manifest { profile: meta, merge, vars, features: declared, files, scaffolds, .. } =
663 manifest;
664 Ok(Layer {
665 rev: reader.rev().cloned(),
666 source,
667 source_name,
668 dir,
669 meta,
670 merge,
671 vars,
672 declared: declared.declared.into_keys().collect(),
673 features,
674 required_by: required_by.into_iter().collect(),
675 configured,
676 manifest: digest,
677 digest,
678 locked,
679 specs: (files, scaffolds),
680 scaffolds: BTreeMap::new(),
681 files: BTreeMap::new(),
682 written,
683 payload: Tree::default(),
684 starters: Tree::default(),
685 })
686 }
687}
688
689fn providers(
696 layers: &[Layer], overrides: &BTreeMap<RelPath, Override>,
697) -> Result<BTreeMap<Slot, Provided>> {
698 let mut owners: BTreeMap<&RelPath, Vec<usize>> = BTreeMap::new();
699 for (index, layer) in layers.iter().enumerate() {
700 for (path, file) in &layer.files {
701 if file.gated.is_none() {
702 owners.entry(path).or_default().push(index);
703 }
704 }
705 }
706 let listed = |path: &RelPath| layers.iter().any(|layer| layer.files.contains_key(path));
707 if let Some(path) = overrides.keys().find(|path| !listed(path)) {
708 let mut provided: Vec<RelPath> =
709 layers.iter().flat_map(|layer| layer.files.keys().cloned()).collect();
710 provided.sort();
711 provided.dedup();
712 return Err(TargetError::StaleOverride { path: path.clone(), provided }.into());
713 }
714 let mut providers = BTreeMap::new();
715 let mut folds = BTreeMap::<String, &RelPath>::new();
716 for (path, candidates) in owners {
717 providers.extend(provide(layers, path, candidates, overrides.get(path))?);
718 if let Some(other) = folds.insert(path.fold(), path)
719 && other != path
720 {
721 return Err(
722 ProfileError::FoldCollision { first: other.clone(), second: path.clone() }.into()
723 );
724 }
725 }
726 Ok(providers)
727}
728
729fn provide(
731 layers: &[Layer], path: &RelPath, mut candidates: Vec<usize>, over: Option<&Override>,
732) -> Result<Vec<(Slot, Provided)>> {
733 let name = |index: usize| layers.get(index).map(|layer| layer.meta.name.clone());
734 let spec = |index: usize| {
735 layers.get(index).and_then(|layer| layer.files.get(path)).map(|file| &file.spec)
736 };
737 let policy = |index: usize| {
738 over.and_then(|o| o.policy)
739 .unwrap_or_else(|| spec(index).map_or_else(Policy::default, FileSpec::policy))
740 };
741 let starts = |index: usize| {
742 spec(index).is_some_and(|spec| match spec.scope {
743 Scope::File => policy(index) == Policy::Once,
744 Scope::Keys | Scope::Block => spec.starter.is_some(),
745 })
746 };
747 let mut starter = None;
748 if let Some(from) = over.and_then(|over| over.from.as_ref()) {
749 let listing: Vec<usize> = (0..layers.len())
750 .filter(|&i| layers.get(i).is_some_and(|l| l.files.contains_key(path)))
751 .collect();
752 if !listing.iter().any(|&i| name(i).as_ref() == Some(from)) {
753 let layers = listing.iter().filter_map(|&i| name(i)).collect();
754 return Err(ProfileError::NotProvided {
755 path: path.clone(),
756 from: from.clone(),
757 layers,
758 }
759 .into());
760 }
761 if let Some(picked) = candidates.iter().copied().find(|&i| name(i).as_ref() == Some(from)) {
763 if starts(picked) {
764 let part = |i: usize| spec(i).is_some_and(|spec| spec.scope != Scope::File);
765 candidates.retain(|&i| i == picked || part(i));
766 starter = Some(picked);
767 } else {
768 candidates = vec![picked];
769 }
770 }
771 }
772 let starts = |index: usize| starter.map_or_else(|| starts(index), |picked| picked == index);
773 let claims: Vec<Claim> = candidates
774 .iter()
775 .filter_map(|&i| {
776 Some(Claim {
777 layer: name(i)?,
778 scope: spec(i)?.scope,
779 policy: policy(i),
780 starts: starts(i),
781 })
782 })
783 .collect();
784 share(path, &claims)?;
785 let mut provided = Vec::with_capacity(candidates.len());
786 for index in candidates {
787 let (Some(spec), Some(owner)) = (spec(index), name(index)) else { continue };
788 let validate = over.and_then(|o| o.validate).or(spec.validate);
789 let format = validate.unwrap_or_else(|| Format::of(path));
790 let shape = Shape::of(spec.scope, path, format, spec.comment, owner.as_str())?;
791 let portion = shape.map(|shape| Portion {
792 scope: spec.scope,
793 shape,
794 content: Vec::new(),
795 payload: Written::default(),
796 text: Vec::new(),
797 starts: starts(index),
798 starter: None,
799 });
800 let slot = Slot { path: path.clone(), part: portion.as_ref().map(|_| owner.to_string()) };
801 let policy = policy(index);
802 provided.push((
803 slot,
804 Provided {
805 layer: index,
806 policy,
807 validate: format,
808 executable: spec.executable,
809 portion,
810 },
811 ));
812 }
813 Ok(provided)
814}
815
816struct Claim {
818 layer: ProfileName,
820 scope: Scope,
822 policy: Policy,
824 starts: bool,
826}
827
828fn share(path: &RelPath, claims: &[Claim]) -> Result<()> {
837 if claims.len() <= 1 {
838 return Ok(());
839 }
840 let named = |keep: &dyn Fn(&Claim) -> bool| -> Vec<ProfileName> {
841 claims.iter().filter(|claim| keep(claim)).map(|claim| claim.layer.clone()).collect()
842 };
843 let wholes = named(&|claim| claim.scope == Scope::File);
844 if wholes.len() > 1 {
845 return Err(ProfileError::Collision { path: path.clone(), layers: wholes }.into());
846 }
847 let mut parts =
848 claims.iter().filter(|claim| claim.scope != Scope::File).map(|claim| claim.scope);
849 let one_scope = parts.next().is_none_or(|first| parts.all(|scope| scope == first));
850 let owned =
851 claims.iter().any(|claim| claim.scope == Scope::File && claim.policy != Policy::Once);
852 if !one_scope || owned {
853 let layers = claims.iter().map(|claim| (claim.layer.clone(), claim.scope)).collect();
854 return Err(ProfileError::Scopes { path: path.clone(), layers }.into());
855 }
856 let starters = named(&|claim| claim.starts);
857 if starters.len() > 1 {
858 return Err(ProfileError::Starters { path: path.clone(), layers: starters }.into());
859 }
860 Ok(())
861}
862
863fn portions(layers: &[Layer], providers: &mut BTreeMap<Slot, Provided>) -> Result<()> {
866 let mut owned: Vec<(&RelPath, usize, Key)> = Vec::new();
867 for (slot, provided) in providers.iter_mut() {
868 let Some(portion) = &mut provided.portion else {
869 continue;
870 };
871 let Some(layer) = layers.get(provided.layer) else {
872 continue;
873 };
874 let bytes = layer.payload.get(&slot.path).ok_or_else(|| ProfileError::MissingPayload {
875 profile: layer.meta.name.clone(),
876 path: slot.path.clone(),
877 })?;
878 let written =
879 layer.files.get(&slot.path).map_or(slot.path.as_str(), |file| file.written.as_str());
880 let label = layer.label(&format!("{PAYLOAD}/{written}"));
881 (portion.content, portion.payload) = portion.shape.read(bytes, &label)?;
882 portion.text = bytes.to_vec();
883 portion.starter =
884 layer.starters.get(&slot.path).filter(|_| portion.starts).map(<[u8]>::to_vec);
885 let keys = portion.shape.keys(&portion.content);
886 owned.extend(keys.into_iter().map(|key| (&slot.path, provided.layer, key)));
887 }
888 owned.sort_by_key(|&(path, layer, _)| (path, layer));
889 let name = |index: usize| layers.get(index).map(|l| l.meta.name.clone());
890 for (at, (path, layer, key)) in owned.iter().enumerate() {
891 let earlier = owned.get(..at).unwrap_or_default().iter().rev();
892 let clash = earlier
893 .take_while(|(other, ..)| other == path)
894 .find(|(_, other, theirs)| other != layer && overlap(key, theirs));
895 if let Some((_, other, theirs)) = clash
896 && let (Some(first), Some(second)) = (name(*other), name(*layer))
897 {
898 let key = display(if key.len() <= theirs.len() { key } else { theirs });
899 return Err(ProfileError::Overlap { path: (*path).clone(), key, first, second }.into());
900 }
901 }
902 Ok(())
903}