Compare commits

..

No commits in common. "61709abeb59c72d3bf02e135fa7179f965d216ae" and "72fe2e96390af085ba89078e8c802d1c03b0ff44" have entirely different histories.

6 changed files with 138 additions and 151 deletions

View file

@ -5,8 +5,6 @@ in
{
imports = with lib.fileset; toList (difference (fileFilter ({ hasExt, ... }: hasExt "nix") ./.) ./default.nix);
collections.news.type = config.content-types.article;
pages.index = {
title = "Fediversity";
locations = [

View file

@ -8,11 +8,7 @@
, lib ? import "${sources.nixpkgs}/lib"
}:
let
lib' = final: prev:
let
new = import ./lib.nix { lib = final; };
in
new // { types = prev.recursiveUpdate prev.types new.types; };
lib' = final: prev: import ./lib.nix { lib = final; };
lib'' = lib.extend lib';
in
{

44
lib.nix
View file

@ -37,48 +37,4 @@ rec {
indent = prefix: s:
join "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
types = rec {
collection = elemType:
let
unparenthesize = class: class == "noun";
desc = type:
types.optionDescriptionPhrase unparenthesize type;
desc' = type:
let
typeDesc = lib.types.optionDescriptionPhrase unparenthesize type;
in
if type.descriptionClass == "noun"
then
typeDesc + "s"
else
"many instances of ${typeDesc}";
in
lib.types.mkOptionType {
name = "collection";
description = "separately specified ${desc elemType} for a collection of ${desc' elemType}";
merge = loc: defs:
map
(def:
let
merged = lib.mergeDefinitions
(loc ++ [ "[definition ${toString def.file}]" ])
elemType
[{ inherit (def) file; value = def.value; }];
in
if merged ? mergedValue then merged.mergedValue else merged.value
)
defs;
check = elemType.check;
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: collection (elemType.substSubModules m);
functor = (lib.defaultFunctor "collection") // {
type = collection;
wrapped = elemType;
payload = { };
};
nestedTypes.elemType = elemType;
};
};
}

View file

@ -1,97 +0,0 @@
{ config, lib, options, ... }:
let
inherit (lib)
mkOption
types
;
cfg = config;
in
{
options = {
content-types = mkOption {
description = "Content types";
type = with types; attrsOf deferredModule;
};
};
config.content-types = {
page = { name, config, ... }: {
options = {
name = mkOption {
description = "Symbolic name for the page, used as a human-readable identifier";
type = types.str;
default = name;
};
title = mkOption {
description = "Page title";
type = types.str;
default = name;
};
locations = mkOption {
description = ''
List of historic output locations for the resulting file
The first element is the canonical location.
All other elements are used to create redirects to the canonical location.
'';
type = with types; nonEmptyListOf str;
};
link = mkOption {
description = "Helper function for transparent linking to other pages";
type = with types; functionTo str;
default = target: "TODO: compute the relative path based on `locations`";
};
outPath = mkOption {
description = ''
Location of the page, used for transparently creating links
'';
type = types.str;
default = lib.head config.locations;
};
description = mkOption {
description = ''
One-sentence description of page contents
'';
type = types.str;
};
summary = mkOption {
description = ''
One-paragraph summary of page contents
'';
type = types.str;
};
body = mkOption {
description = ''
Page contents in CommonMark
'';
type = types.str;
};
template = mkOption {
description = ''
Function that converts the page contents to files
'';
type = with types; functionTo (functionTo options.files.type);
default = cfg.templates.page;
};
};
};
article = { config, collectionName, ... }: {
imports = [ cfg.content-types.page ];
options = {
date = mkOption {
description = "Publication date";
type = with types; nullOr str;
default = null;
};
author = mkOption {
description = "Page author";
type = with types; nullOr (either str (listOf str));
default = null;
};
};
config.name = lib.slug config.title;
config.outPath = "${collectionName}/${lib.head config.locations}";
config.template = cfg.templates.article;
};
};
}

View file

@ -5,15 +5,101 @@ let
types
;
cfg = config;
types' = import ./types.nix { inherit lib; } // {
article = { config, collectionName, ... }: {
imports = [ types'.page ];
options = {
date = mkOption {
description = "Publication date";
type = with types; nullOr str;
default = null;
};
author = mkOption {
description = "Page author";
type = with types; nullOr (either str (listOf str));
default = null;
};
};
config.name = lib.slug config.title;
config.outPath = "${collectionName}/${lib.head config.locations}";
config.template = cfg.templates.article;
};
page = { name, config, ... }: {
options = {
name = mkOption {
description = "Symbolic name for the page, used as a human-readable identifier";
type = types.str;
default = name;
};
title = mkOption {
description = "Page title";
type = types.str;
default = name;
};
locations = mkOption {
description = ''
List of historic output locations for the resulting file
The first element is the canonical location.
All other elements are used to create redirects to the canonical location.
'';
type = with types; nonEmptyListOf str;
};
link = mkOption {
description = "Helper function for transparent linking to other pages";
type = with types; functionTo str;
default = target: "TODO: compute the relative path based on `locations`";
};
outPath = mkOption {
description = ''
Location of the page, used for transparently creating links
'';
type = types.str;
default = lib.head config.locations;
};
description = mkOption {
description = ''
One-sentence description of page contents
'';
type = types.str;
};
summary = mkOption {
description = ''
One-paragraph summary of page contents
'';
type = types.str;
};
body = mkOption {
description = ''
Page contents in CommonMark
'';
type = types.str;
};
template = mkOption
{
description = ''
Function that converts the page contents to files
'';
type = with types; functionTo (functionTo options.files.type);
default = cfg.templates.page;
};
};
};
};
in
{
imports = [ ./content-types.nix ];
# TODO: split out:
# - extra module system types into lib'
# - page and article types into their own module values under structure/${page,article}.nix
# yes, actually. those types should probably be configurable
config.collections.news.type = types'.article;
options.pages = mkOption {
description = ''
Collection of pages on the site
'';
type = with types; attrsOf (submodule config.content-types.page);
type = with types; attrsOf (submodule types'.page);
};
options.collections = mkOption
@ -29,7 +115,7 @@ in
};
entry = mkOption {
description = "An entry in the collection";
type = types.collection (types.submodule ({
type = types'.collection (types.submodule ({
_module.args.collection = config.entry;
_module.args.collectionName = name;
imports = [ config.type ];
@ -136,6 +222,7 @@ in
in
pages // collections;
options.build = mkOption {
description = ''
The final output of the web site

47
structure/types.nix Normal file
View file

@ -0,0 +1,47 @@
{ lib, ... }:
let
inherit (lib) types;
in
rec {
collection = elemType:
let
unparenthesize = class: class == "noun";
desc = type:
types.optionDescriptionPhrase unparenthesize type;
desc' = type:
let
typeDesc = types.optionDescriptionPhrase unparenthesize type;
in
if type.descriptionClass == "noun"
then
typeDesc + "s"
else
"many instances of ${typeDesc}";
in
types.mkOptionType {
name = "collection";
description = "separately specified ${desc elemType} for a collection of ${desc' elemType}";
merge = loc: defs:
map
(def:
let
merged = lib.mergeDefinitions
(loc ++ [ "[definition ${toString def.file}]" ])
elemType
[{ inherit (def) file; value = def.value; }];
in
if merged ? mergedValue then merged.mergedValue else merged.value
)
defs;
check = elemType.check;
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: collection (elemType.substSubModules m);
functor = (lib.defaultFunctor "collection") // {
type = collection;
wrapped = elemType;
payload = { };
};
nestedTypes.elemType = elemType;
};
}