2022-10-26 11:16:04 +00:00
{ config , lib , flake-parts-lib , . . . }:
let
inherit ( lib )
filterAttrs
mapAttrs
mkOption
types
;
2024-01-11 11:16:42 +00:00
inherit ( lib . strings )
escapeNixIdentifier
;
2022-10-26 12:07:24 +00:00
transpositionModule = {
options = {
adHoc = mkOption {
type = types . bool ;
default = false ;
2022-11-11 06:39:25 +00:00
description = ''
2022-11-11 05:40:37 +00:00
Whether to provide a stub option declaration for { option } ` perSystem . <name> ` .
2022-10-26 12:07:24 +00:00
The stub option declaration does not support merging and lacks
2022-11-11 05:40:37 +00:00
documentation , so you are recommended to declare the { option } ` perSystem . <name> `
option yourself and avoid { option } ` adHoc ` .
2022-10-26 12:07:24 +00:00
'' ;
} ;
} ;
} ;
2024-01-11 11:16:42 +00:00
perInputAttributeError = { flake , attrName , system , attrConfig }:
# This uses flake.outPath for lack of a better identifier.
# Consider adding a perInput variation that has a normally-redundant argument for the input name.
# Tested manually with
# perSystem = { inputs', ... }: {
# packages.extra = inputs'.nixpkgs.extra;
# packages.default = inputs'.nixpkgs.packages.default;
# packages.veryWrong = (top.config.perInput "x86_64-linux" inputs'.nixpkgs.legacyPackages.hello).packages.default;
# };
# transposition.extra = {};
let
attrPath = " ${ escapeNixIdentifier attrName } . ${ escapeNixIdentifier system } " ;
flakeIdentifier =
if flake . _type or null != " f l a k e "
then
throw " A n a t t e m p t w a s m a d e t o a c c e s s a t t r i b u t e ${ attrPath } o n a v a l u e t h a t ' s s u p p o s e d t o b e a f l a k e , b u t m a y n o t b e a p r o p e r f l a k e . "
else
builtins . addErrorContext " w h i l e t r y i n g t o f i n d o u t h o w t o d e s c r i b e w h a t i s s u p p o s e d l y a f l a k e , w h o s e a t t r i b u t e ${ attrPath } w a s a c c e s s e d b u t d o e s n o t e x i s t " (
toString flake . outPath
) ;
# This ought to be generalized by extending attrConfig, but this is the only known and common mistake for now.
alternateAttrNameHint =
if attrName == " p a c k a g e s " && flake ? legacyPackages
then # Unfortunately we can't just switch them out, because that will put packages *sets* where single packages are expected in user code, resulting in potentially much worse and more confusing errors down the line.
" \n I t d o e s d e f i n e l e g a c y P a c k a g e s ; t r y t h a t i n s t e a d ? "
else " " ;
in
if flake ? ${ attrName }
then
throw ''
Attempt to access $ { attrPath } of flake $ { flakeIdentifier } , but it does not have it .
It does have attribute $ { escapeNixIdentifier attrName } , so it appears that it does not support system type $ { escapeNixIdentifier system } .
''
else
throw ''
Attempt to access $ { attrPath } of flake $ { flakeIdentifier } , but it does not have attribute $ { escapeNixIdentifier attrName } . ${ alternateAttrNameHint }
'' ;
2022-10-26 11:16:04 +00:00
in
{
options = {
transposition = lib . mkOption {
2022-11-11 06:39:25 +00:00
description = ''
2022-10-26 11:16:04 +00:00
A helper that defines transposed attributes in the flake outputs .
2023-10-01 12:34:27 +00:00
When you define ` transposition . foo = { } ; ` , definitions are added to the effect of ( pseudo-code ) :
` ` ` nix
flake . foo . '' ${ system } = ( p e r S y s t e m s y s t e m ) . f o o ;
perInput = system : inputFlake : inputFlake . foo . '' ${ system } ;
` ` `
2022-10-26 11:16:04 +00:00
Transposition is the operation that swaps the indices of a data structure .
Here it refers specifically to the transposition between
2022-11-11 05:40:37 +00:00
` ` ` plain
perSystem : . '' ${ system } . ''$ { a t t r i b u t e }
outputs : . '' ${ attribute } . ''$ { s y s t e m }
` ` `
2022-10-26 11:16:04 +00:00
2022-11-11 05:40:37 +00:00
It also defines the reverse operation in [ { option } ` perInput ` ] ( #opt-perInput).
2022-10-26 11:16:04 +00:00
'' ;
type =
types . lazyAttrsOf
2022-10-26 12:07:24 +00:00
( types . submoduleWith { modules = [ transpositionModule ] ; } ) ;
2022-10-26 11:16:04 +00:00
} ;
} ;
config = {
flake =
lib . mapAttrs
( attrName : attrConfig :
mapAttrs
( system : v : v . ${ attrName } )
config . allSystems
)
config . transposition ;
perInput =
system : flake :
mapAttrs
2023-12-10 13:07:51 +00:00
( attrName : attrConfig :
2024-01-11 11:16:42 +00:00
flake . ${ attrName } . ${ system } or (
throw ( perInputAttributeError { inherit system flake attrName attrConfig ; } )
)
2023-12-10 13:07:51 +00:00
)
2023-05-29 17:39:48 +00:00
config . transposition ;
2022-10-26 12:07:24 +00:00
2023-05-29 17:52:03 +00:00
perSystem = {
2022-10-26 12:07:24 +00:00
options =
mapAttrs
( k : v : lib . mkOption { } )
( filterAttrs
( k : v : v . adHoc )
config . transposition
) ;
} ;
2022-10-26 11:16:04 +00:00
} ;
}