oi_pkg_checker_core/packages/
components.rs

1use std::{
2    cmp::Ordering,
3    collections::{HashMap, HashSet},
4    fmt::Debug,
5};
6
7use fmri::{FMRIList, FMRI};
8
9use crate::problems::Problem::SamePackageHasTwoPublishers;
10use crate::{
11    clone, downgrade, get, get_mut, new,
12    packages::{
13        dependency_type::{
14            DependencyTypes,
15            DependencyTypes::{Build, Runtime, SystemBuild, SystemTest, Test},
16        },
17        package::Package,
18        rev_depend_type::{RevDependType, RevDependType::*},
19    },
20    problems::{
21        Problem,
22        Problem::{
23            MissingComponentForPackage, NonExistingPackageInPkg5, NonExistingRequired,
24            NonExistingRequiredByRenamed, ObsoletedPackageInComponent, ObsoletedRequired,
25            ObsoletedRequiredByRenamed, PartlyObsoletedRequired, PartlyObsoletedRequiredByRenamed,
26            RenamedNeedsRenamed, RenamedPackageInComponent, UselessComponent,
27        },
28    },
29    shared_type, weak_type, DependTypes, Problems,
30};
31
32#[derive(Default, Clone, Debug)]
33pub struct Components {
34    /// components in system
35    pub(crate) components: Vec<shared_type!(Component)>,
36    pub(crate) hash_components: HashMap<String, shared_type!(Component)>,
37    /// packages in system
38    pub(crate) packages: Vec<shared_type!(Package)>,
39    pub(crate) hash_packages: HashMap<String, shared_type!(Package)>,
40    pub problems: Problems,
41}
42
43impl Components {
44    pub fn add_package(&mut self, mut package: Package) {
45        let package_name = package.fmri.clone().get_package_name_as_string();
46
47        let mut existing_package = get_mut!(match self.get_package_by_fmri(&package.fmri) {
48            Ok(e) => e,
49            Err(_) => {
50                let rc_package = new!(package);
51                self.packages.push(clone!(&rc_package));
52                self.hash_packages.insert(package_name, rc_package);
53                return;
54            }
55        });
56
57        let mut existing_package_versions = existing_package.get_versions().clone();
58        existing_package_versions.sort_by(|a, b| a.version.cmp(&b.version));
59        package.versions.sort_by(|a, b| a.version.cmp(&b.version));
60
61        let o = existing_package_versions.first().unwrap();
62        let n = package.versions.first().unwrap();
63
64        match (o.is_obsolete(), n.is_obsolete()) {
65            (true, false) => {
66                match o.version.cmp(&n.version) {
67                    Ordering::Equal | Ordering::Less => {
68                        // everything is ok, old version is obsoleted, but we need to save new version
69
70                        *existing_package = package;
71                    }
72                    Ordering::Greater => {
73                        // this is problem, newer version is obsoleted, but older has to be obsoleted
74
75                        let p_a = existing_package.fmri.clone().get_publisher().unwrap();
76                        let p_b = package.fmri.clone().get_publisher().unwrap();
77
78                        drop(existing_package);
79
80                        self.problems.add_problem(SamePackageHasTwoPublishers(
81                            package.fmri.clone(),
82                            p_a.clone(),
83                            p_b.clone(),
84                            None,
85                        ));
86                    }
87                }
88            }
89            (false, true) => {
90                match o.version.cmp(&n.version) {
91                    Ordering::Equal | Ordering::Less => {
92                        // this is problem, newer version is obsoleted, but older has to be obsoleted
93
94                        let p_a = existing_package.fmri.clone().get_publisher().unwrap();
95                        let p_b = package.fmri.clone().get_publisher().unwrap();
96
97                        drop(existing_package);
98
99                        self.problems.add_problem(SamePackageHasTwoPublishers(
100                            package.fmri.clone(),
101                            p_a.clone(),
102                            p_b.clone(),
103                            None,
104                        ));
105                    }
106                    Ordering::Greater => {
107                        // everything is ok, old version is obsoleted
108                    }
109                }
110            }
111            (false, false) => {
112                // this is problem, one of them must be obsoleted
113
114                let p_a = existing_package.fmri.clone().get_publisher().unwrap();
115                let p_b = package.fmri.clone().get_publisher().unwrap();
116
117                drop(existing_package);
118
119                self.problems.add_problem(SamePackageHasTwoPublishers(
120                    package.fmri.clone(),
121                    p_a.clone(),
122                    p_b.clone(),
123                    Some(match o.version.cmp(&n.version) {
124                        Ordering::Equal => p_a,
125                        Ordering::Greater | Ordering::Less => p_b,
126                    }),
127                ));
128            }
129            (true, true) => {
130                // everything is ok, basically the whole package is obsoleted
131            }
132        }
133    }
134
135    pub fn new_component(
136        &mut self,
137        component_name: String,
138        packages: Vec<FMRI>,
139    ) -> Result<(), String> {
140        let rc_component = new!(Component::new(component_name.clone()));
141
142        for fmri in packages {
143            let res = match self.get_package_by_fmri(&fmri) {
144                Ok(rc_package) => {
145                    get_mut!(rc_component).add_package(downgrade!(rc_package));
146                    get_mut!(rc_package).set_component(clone!(&rc_component))
147                }
148                Err(_) => Some(Box::new(NonExistingPackageInPkg5(
149                    fmri,
150                    component_name.clone(),
151                ))),
152            };
153
154            if let Some(p) = res {
155                self.problems.add_problem(*p);
156            }
157        }
158
159        self.components.push(clone!(&rc_component));
160        self.hash_components.insert(component_name, rc_component);
161
162        Ok(())
163    }
164
165    pub fn get_component_by_name(&self, name: &String) -> Result<&shared_type!(Component), String> {
166        return match self.hash_components.get(name) {
167            None => Err(format!("component {} does not exist", name)),
168            Some(component) => Ok(component),
169        };
170    }
171
172    pub fn get_package_by_fmri(&self, fmri: &FMRI) -> Result<&shared_type!(Package), String> {
173        return match self
174            .hash_packages
175            .get(fmri.get_package_name_as_ref_string())
176        {
177            None => Err(format!("package {} does not exist", fmri)),
178            Some(package) => Ok(package),
179        };
180    }
181
182    pub fn get_components(&self) -> &Vec<shared_type!(Component)> {
183        &self.components
184    }
185
186    pub fn get_packages(&self) -> &Vec<shared_type!(Package)> {
187        &self.packages
188    }
189
190    /// adds repo dependencies (Build, Test, System Build and System Test) into component
191    pub fn add_repo_dependencies(
192        &mut self,
193        component_name: &String,
194        dependencies: Vec<FMRI>,
195        dependency_type: &DependencyTypes,
196    ) -> Result<(), String> {
197        for fmri in dependencies {
198            let rc_package = if let Ok(p) = self.get_package_by_fmri(&fmri) {
199                p
200            } else {
201                self.problems.add_problem(NonExistingRequired(
202                    DependTypes::Require(fmri),
203                    dependency_type.clone(),
204                    FMRI::parse_raw("none").unwrap(),
205                    component_name.clone(),
206                ));
207
208                continue;
209            };
210
211            let component = self
212                .get_component_by_name(component_name)
213                .map_err(|e| format!("failed to get component: {}", e))?;
214
215            let mut component_mut = get_mut!(component);
216
217            match dependency_type {
218                Build => component_mut.build.push(downgrade!(rc_package)),
219                Test => component_mut.test.push(downgrade!(rc_package)),
220                SystemBuild => component_mut.sys_build.push(downgrade!(rc_package)),
221                SystemTest => component_mut.sys_test.push(downgrade!(rc_package)),
222                Runtime => {
223                    return Err("can not insert runtime dependencies into component".to_owned())
224                }
225            }
226
227            get_mut!(rc_package)
228                .add_dependent(clone!(component), dependency_type)
229                .map_err(|e| format!("failed to add dependent: {}", e))?;
230        }
231
232        Ok(())
233    }
234
235    pub fn set_package_obsolete(&mut self, fmri: FMRI) -> Result<(), String> {
236        let mut fmri_clone = fmri.clone();
237        let rc_package = self
238            .get_package_by_fmri(fmri_clone.remove_version())
239            .map_err(|e| format!("failed to get package: {}", e))?;
240
241        match fmri.get_version() {
242            None => get_mut!(rc_package).set_obsolete(true),
243            Some(fmri_version) => {
244                for version in get_mut!(rc_package).get_versions_mut() {
245                    if version.version == fmri_version {
246                        version.set_obsolete(true);
247                    }
248                }
249            }
250        }
251
252        Ok(())
253    }
254
255    pub fn set_package_renamed(&mut self, fmri: FMRI) -> Result<(), String> {
256        let mut fmri_clone = fmri.clone();
257        let rc_package = self
258            .get_package_by_fmri(fmri_clone.remove_version())
259            .map_err(|e| format!("failed to get package: {}", e))?;
260
261        match fmri.get_version() {
262            None => get_mut!(rc_package).set_renamed(true),
263            Some(fmri_version) => {
264                for version in get_mut!(rc_package).get_versions_mut() {
265                    if version.version == fmri_version {
266                        version.set_renamed(true);
267                    }
268                }
269            }
270        }
271
272        Ok(())
273    }
274
275    // TODO: there might be something wrong here
276    pub fn distribute_reverse_runtime_dependencies(&mut self) {
277        let mut rev_run_deps: HashMap<FMRI, HashSet<RevDependType>> = HashMap::new();
278
279        let mut add = |fmri: FMRI, rev_depend_type: RevDependType| {
280            rev_run_deps
281                .entry(fmri)
282                .or_default()
283                .insert(rev_depend_type);
284        };
285
286        for p in &*self.packages {
287            let package = get!(p);
288            for version in &package.versions {
289                for d in &version.runtime {
290                    match d.clone() {
291                        DependTypes::Require(f) => add(f, Require(package.fmri.clone())),
292                        DependTypes::Optional(f) => add(f, Optional(package.fmri.clone())),
293                        DependTypes::Incorporate(f) => add(f, Incorporate(package.fmri.clone())),
294                        DependTypes::RequireAny(l) => {
295                            for f in l.get() {
296                                add(f, Require(package.fmri.clone()))
297                            }
298                        }
299                        DependTypes::Conditional(f, p) => {
300                            add(f, ConditionalFmri(package.fmri.clone()));
301                            add(p, ConditionalPredicate(package.fmri.clone()));
302                        }
303                        DependTypes::Group(f) => add(f, Group(package.fmri.clone())),
304                        _ => unimplemented!(),
305                    };
306                }
307            }
308        }
309
310        for (fmri, hash_rev_deps) in rev_run_deps {
311            let mut rev_deps = hash_rev_deps
312                .iter()
313                .cloned()
314                .collect::<Vec<RevDependType>>();
315
316            match self.get_package_by_fmri(&fmri) {
317                Ok(package) => get_mut!(package).runtime_dependents.append(&mut rev_deps),
318                Err(_) => {
319                    for rev_dep in rev_deps {
320                        let (f, d_type) = match rev_dep {
321                            Require(f) => (f, DependTypes::Require(fmri.clone())),
322                            Optional(f) => (f, DependTypes::Optional(fmri.clone())),
323                            Incorporate(f) => (f, DependTypes::Incorporate(fmri.clone())),
324                            RequireAny(f) => (
325                                f,
326                                DependTypes::RequireAny(FMRIList::from(vec![fmri.clone()])),
327                            ),
328                            ConditionalFmri(f) => (
329                                f,
330                                DependTypes::Conditional(
331                                    fmri.clone(),
332                                    FMRI::parse_raw("none").unwrap(),
333                                ),
334                            ),
335                            ConditionalPredicate(f) => (
336                                f,
337                                DependTypes::Conditional(
338                                    FMRI::parse_raw("none").unwrap(),
339                                    fmri.clone(),
340                                ),
341                            ),
342                            Group(f) => (f, DependTypes::Group(fmri.clone())),
343                        };
344
345                        self.problems
346                            .add_problem(match self.get_package_by_fmri(&f) {
347                                Ok(p) => match get!(p).is_renamed() {
348                                    true => NonExistingRequiredByRenamed(d_type, Runtime, f),
349                                    false => NonExistingRequired(d_type, Runtime, f, "".to_owned()),
350                                },
351                                Err(_) => {
352                                    panic!("non existing as required by non existing?")
353                                }
354                            });
355                    }
356                }
357            }
358        }
359    }
360
361    pub fn remove_old_versions(&mut self) {
362        for p in &mut self.packages {
363            let mut package = get_mut!(p);
364
365            package.versions.sort_by(|a, b| b.version.cmp(&a.version));
366
367            let mut new_ver = package.versions.first().unwrap().clone();
368
369            for ver in &package.versions {
370                if !ver.is_obsolete() && !ver.is_renamed() {
371                    new_ver = ver.clone();
372                    break;
373                }
374            }
375
376            package.change_versions(vec![new_ver]);
377        }
378    }
379
380    pub fn check_problems(&mut self) -> Result<(), String> {
381        // ObsoletedPackageInComponent and RenamedPackageInComponent
382        for c in &*self.components {
383            let component = get!(c);
384            for p in &component.packages {
385                let t = p.upgrade().unwrap();
386                let package = get!(t);
387                if package.is_obsolete() {
388                    self.problems.add_problem(ObsoletedPackageInComponent(
389                        package.fmri.clone(),
390                        component.name.clone(),
391                    ));
392                } else if package.is_renamed() {
393                    self.problems.add_problem(RenamedPackageInComponent(
394                        package.fmri.clone(),
395                        component.name.clone(),
396                    ));
397                }
398            }
399        }
400
401        // MissingComponentForPackage
402        for p in &*self.packages {
403            let package = get!(p);
404
405            if package.is_in_component().is_none()
406                && !package.is_renamed()
407                && !package.is_obsolete()
408            {
409                self.problems
410                    .add_problem(MissingComponentForPackage(package.fmri.clone()));
411            }
412        }
413
414        // UselessComponent
415        'main: for c in &*self.components {
416            let component = get!(c);
417            if component.packages.iter().all(|p| {
418                let tmp = p.upgrade().unwrap();
419                let package = get!(tmp);
420
421                if package.is_obsolete() || package.is_renamed() {
422                    return false;
423                }
424
425                for dep in &package.runtime_dependents {
426                    if let Incorporate(_) = dep {
427                    } else {
428                        return false;
429                    }
430                }
431
432                if package.build_dependents.is_empty()
433                    && package.test_dependents.is_empty()
434                    && package.sys_build_dependents.is_empty()
435                    && package.sys_build_dependents.is_empty()
436                {
437                    return true;
438                }
439
440                false
441            }) {
442                self.problems
443                    .add_problem(UselessComponent(component.get_name().clone()));
444            } else {
445                let name = component.name.clone();
446                let packages_fmris = component
447                    .packages
448                    .iter()
449                    .map(|p| get!(p.upgrade().unwrap()).fmri.clone())
450                    .collect::<Vec<FMRI>>();
451
452                let packages = component.packages.clone();
453
454                drop(component);
455
456                for p in packages.iter().map(|a| a.upgrade().unwrap()) {
457                    let package = get!(p);
458
459                    for a in &package.runtime_dependents {
460                        match a {
461                            Require(f)
462                            | Optional(f)
463                            | RequireAny(f)
464                            | ConditionalFmri(f)
465                            | ConditionalPredicate(f)
466                            | Group(f) => {
467                                if !packages_fmris.contains(f) {
468                                    continue 'main;
469                                }
470                            }
471                            Incorporate(_) => {}
472                        }
473                    }
474
475                    let check = |deps: &Vec<shared_type!(Component)>| -> bool {
476                        !deps.iter().all(|c| name == get!(c).name)
477                    };
478
479                    if check(&package.build_dependents)
480                        || check(&package.sys_build_dependents)
481                        || check(&package.test_dependents)
482                        || check(&package.sys_test_dependents)
483                    {
484                        continue 'main;
485                    }
486                }
487
488                self.problems.add_problem(UselessComponent(name));
489            }
490        }
491
492        // RenamedNeedsRenamed
493        for p in &*self.packages {
494            let package = get!(p);
495
496            if !package.is_renamed() {
497                continue;
498            }
499
500            for rev_dep in &package.runtime_dependents {
501                match rev_dep {
502                    Require(fmri)
503                    | Optional(fmri)
504                    | Incorporate(fmri)
505                    | RequireAny(fmri)
506                    | ConditionalFmri(fmri)
507                    | ConditionalPredicate(fmri)
508                    | Group(fmri) => {
509                        let package_b = self
510                            .get_package_by_fmri(fmri)
511                            .map_err(|e| format!("failed to get package: {}", e))?;
512                        if !get!(package_b).is_renamed() {
513                            continue;
514                        }
515                        let fmri_b = get!(package_b).fmri.clone();
516                        self.problems
517                            .add_problem(RenamedNeedsRenamed(fmri_b, package.fmri.clone()));
518                    }
519                }
520            }
521
522            match &package.component {
523                None => {}
524                Some(c) => {
525                    let component = get!(c);
526
527                    let mut check_dependencies = |dependencies: &Vec<weak_type!(Package)>| {
528                        for dep in dependencies {
529                            let p = dep.upgrade().unwrap();
530                            let package_b = get!(p);
531                            if package_b.is_renamed() {
532                                self.problems.add_problem(RenamedNeedsRenamed(
533                                    package.fmri.clone(),
534                                    package_b.fmri.clone(),
535                                ));
536                            }
537                        }
538                    };
539
540                    check_dependencies(&component.build);
541                    check_dependencies(&component.test);
542                    check_dependencies(&component.sys_build);
543                    check_dependencies(&component.sys_test);
544                }
545            }
546        }
547
548        // ObsoletedRequired, ObsoletedRequiredByRenamed, PartlyObsoletedRequired, PartlyObsoletedRequiredByRenamed
549        for p in &self.packages.clone() {
550            let package = get!(p);
551
552            if !package.is_obsolete() {
553                continue;
554            }
555
556            if package.versions.first().unwrap().is_obsolete() {
557                check_obsoleted_required_packages(
558                    self,
559                    &package,
560                    ObsoletedRequired,
561                    ObsoletedRequiredByRenamed,
562                );
563            } else {
564                check_obsoleted_required_packages(
565                    self,
566                    &package,
567                    PartlyObsoletedRequired,
568                    PartlyObsoletedRequiredByRenamed,
569                );
570            }
571        }
572
573        Ok(())
574    }
575}
576
577fn check_obsoleted_required_packages(
578    components: &mut Components,
579    package: &Package,
580    problem_type: fn(DependTypes, DependencyTypes, FMRI, String) -> Problem,
581    problem_type_renamed: fn(DependTypes, DependencyTypes, FMRI) -> Problem,
582) {
583    let mut check = |deps: &Vec<shared_type!(Component)>, dt: DependencyTypes| {
584        for c in deps {
585            components.problems.add_problem(problem_type(
586                DependTypes::Require(package.fmri.clone()),
587                dt.clone(),
588                FMRI::parse_raw("none").unwrap(),
589                get!(c).name.clone(),
590            ));
591        }
592    };
593
594    check(&package.build_dependents, Build);
595    check(&package.sys_build_dependents, SystemBuild);
596    check(&package.test_dependents, Test);
597    check(&package.sys_test_dependents, SystemTest);
598
599    for d in &package.runtime_dependents {
600        let required_by_fmri = match d {
601            Require(fmri)
602            | Optional(fmri)
603            | RequireAny(fmri)
604            | ConditionalFmri(fmri)
605            | ConditionalPredicate(fmri)
606            | Group(fmri) => fmri.clone(),
607            Incorporate(_) => continue,
608        };
609
610        let p = get!(components.get_package_by_fmri(&required_by_fmri).unwrap());
611        let o = p.is_obsolete();
612        let r = p.is_renamed();
613        drop(p);
614
615        if o {
616            continue;
617        } else if r {
618            components.problems.add_problem(problem_type_renamed(
619                DependTypes::Require(package.fmri.clone()),
620                Runtime,
621                required_by_fmri,
622            ));
623        } else {
624            components.problems.add_problem(problem_type(
625                DependTypes::Require(package.fmri.clone()),
626                Runtime,
627                required_by_fmri,
628                "".to_owned(),
629            ));
630        }
631    }
632}
633
634/// Component contains name, list of packages in component and dependencies.
635#[derive(Clone, Debug)]
636pub struct Component {
637    pub(crate) name: String,
638    /// contains no version
639    pub(crate) packages: Vec<weak_type!(Package)>,
640    /// dependencies
641    pub(crate) build: Vec<weak_type!(Package)>,
642    pub(crate) test: Vec<weak_type!(Package)>,
643    pub(crate) sys_build: Vec<weak_type!(Package)>,
644    pub(crate) sys_test: Vec<weak_type!(Package)>,
645}
646
647impl Component {
648    pub fn new(component_name: String) -> Self {
649        Self {
650            name: component_name,
651            packages: Vec::new(),
652            build: Vec::new(),
653            test: Vec::new(),
654            sys_build: Vec::new(),
655            sys_test: Vec::new(),
656        }
657    }
658
659    fn add_package(&mut self, package: weak_type!(Package)) {
660        self.packages.push(package)
661    }
662
663    pub fn get_name(&self) -> &String {
664        &self.name
665    }
666
667    pub fn get_build_dependencies(&self) -> &Vec<weak_type!(Package)> {
668        &self.build
669    }
670
671    pub fn get_sys_build_dependencies(&self) -> &Vec<weak_type!(Package)> {
672        &self.sys_build
673    }
674
675    pub fn get_test_dependencies(&self) -> &Vec<weak_type!(Package)> {
676        &self.test
677    }
678
679    pub fn get_sys_test_dependencies(&self) -> &Vec<weak_type!(Package)> {
680        &self.sys_test
681    }
682}