#!/usr/bin/env python3

#Unit test against forbidden dependencies (this helps us to remember to update
#dependencies if e.g. a package moves from Projects/ to Framework/).

import simplebuild.cfg
pkgs = simplebuild.cfg.pkgs


def pkg_dep_iter(pkgdata,direct_deps_only=False):
    for pkgdepname in pkgdata.get('deps_pkgs_direct' if direct_deps_only else 'deps_pkgs',[]):
        yield pkgs[pkgdepname]

def check_rule_impl(pkgselectfilter,trouble_dep_filter,rule_description,_direct_deps_only):
    for _,pkgdata in sorted(pkgs.items()):
        if not pkgselectfilter(pkgdata):
            continue
        for _,pkg_dep in sorted( (p['name'],p) for p in  pkg_dep_iter(pkgdata,direct_deps_only=_direct_deps_only)):
            if trouble_dep_filter(pkg_dep):
                raise SystemExit('ERROR: Package %s is not allowed to depend on package %s (%s)'%(pkgdata['name'],
                                                                                                  pkg_dep['name'],
                                                                                                  rule_description))

def check_rule(*args,**kwargs):
    #first check only direct deps, for more useful error messages:
    check_rule_impl(*args,**kwargs,_direct_deps_only=True)
    check_rule_impl(*args,**kwargs,_direct_deps_only=False)


def fct_negate( fct ):
    return ( lambda pd : not fct(pd) )
def fct_or( fcta, fctb ):
    return ( lambda pd : ( fcta(pd) or fctb(pd) ) )

select_fmwk = lambda pd : pd['reldirname'].startswith('Framework/')
select_fmwkattic = lambda pd : pd['reldirname'].startswith('Projects/FmwkAttic/')
select_fmwk_or_fmwkattic = (lambda pd : select_fmwkattic(pd) or select_fmwk(pd))

select_non_fmwk = lambda pd : not select_fmwk(pd)
select_val = lambda pd : pd['reldirname'].startswith('Validation/')

def select_named_pkg(pkgname):
    return lambda pd : pd['name']==pkgname

check_rule( select_fmwk,
            fct_negate(select_fmwk),
            'Framework/ packages are not allowed to depend on non-Framework pkgs.' )
check_rule( select_fmwkattic,
            fct_negate(fct_or(select_fmwk,select_fmwkattic)),
            'Projects/FmwkAttic/ packages are only allowed to depend on other Projects/FmwkAttic/ pkgs or Framework/ pkgs.' )
check_rule( select_val,
            fct_negate(fct_or(select_val,select_fmwk)),
            'Validation/ packages are only allowed to depend on other Validation/ pkgs or Framework/ pkgs.' )
check_rule( fct_negate(select_val),
            select_val,
            'No packages outside Validation/ should depend on pkgs in Validation/.' )

core_pkg_deps = []
check_rule( select_named_pkg('Core'),
            lambda pd : pd['name'] not in core_pkg_deps,
            'Core package can only have the dependencies %s'%(','.join(core_pkg_deps)))
for pn in core_pkg_deps:
    check_rule( select_named_pkg(pn),
                lambda pd : True,
                f'Package {pn} should have no dependencies.' )
