# The MIT License (MIT) # # Copyright (c) 2019 Vincent Ambo # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. { ... }: args: initPath: let inherit (builtins) attrNames baseNameOf filter hasAttr head isAttrs length listToAttrs map match readDir substring; argsWithPath = parts: let meta.locatedAt = parts; in meta // (if isAttrs args then args else args meta); readDirVisible = path: let children = readDir path; isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != "."; names = filter isVisible (attrNames children); in listToAttrs (map (name: { inherit name; value = children.${name}; }) names); # The marker is added to every set that was imported directly by # readTree. importWithMark = path: parts: let imported = import path (argsWithPath parts); in if (isAttrs imported) then imported // { __readTree = true; } else imported; nixFileName = file: let res = match "(.*)\.nix" file; in if res == null then null else head res; readTree = path: parts: let dir = readDirVisible path; self = importWithMark path parts; joinChild = c: path + ("/" + c); # Import subdirectories of the current one, unless the special # `.skip-subtree` file exists which makes readTree ignore the # children. # # This file can optionally contain information on why the tree # should be ignored, but its content is not inspected by # readTree filterDir = f: dir."${f}" == "directory"; children = if hasAttr ".skip-subtree" dir then [] else map (c: { name = c; value = readTree (joinChild c) (parts ++ [ c ]); }) (filter filterDir (attrNames dir)); # Import Nix files nixFiles = filter (f: f != null) (map nixFileName (attrNames dir)); nixChildren = map (c: let p = joinChild (c + ".nix"); in { name = c; value = importWithMark p (parts ++ [ c ]); }) nixFiles; in if dir ? "default.nix" then (if isAttrs self then self // (listToAttrs children) else self) else listToAttrs (nixChildren ++ children); in readTree initPath [ (baseNameOf initPath) ]