Initialize with rules, test data and license from nix-gitignore

This commit is contained in:
siers 2019-05-12 11:51:48 +02:00 committed by Robert Hensing
commit 25093d4d10
3 changed files with 205 additions and 0 deletions

24
LICENSE Normal file
View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org>

93
rules.nix Normal file
View file

@ -0,0 +1,93 @@
{ lib ? import <nixpkgs/lib> }:
/* The functions in this file translate gitignore files into filter functions.
*/
let
inherit (builtins) compareVersions nixVersion split match;
inherit (lib) elemAt length head filter isList;
inherit (lib.strings) substring stringLength replaceStrings concatStringsSep;
debug = a: builtins.trace a a;
last = l: elemAt l ((length l) - 1);
throwIfOldNix = let required = "2.0"; in
if compareVersions nixVersion required == -1
then throw "nix (v${nixVersion} =< v${required}) is too old for nix-gitignore"
else true;
in
rec {
# [["good/relative/source/file" true] ["bad.tmpfile" false]] -> root -> path
filterPattern = patterns: root:
(name: _type:
let
relPath = lib.removePrefix ((toString root) + "/") name;
matches = pair: (match (head pair) relPath) != null;
matched = map (pair: [(matches pair) (last pair)]) patterns;
in
last (last ([[true true]] ++ (filter head matched)))
);
# string -> [[regex bool]]
gitignoreToPatterns = gitignore:
assert throwIfOldNix;
let
# ignore -> bool
isComment = i: (match "^(#.*|$)" i) != null;
# ignore -> [ignore bool]
computeNegation = l:
let split = match "^(!?)(.*)" l;
in [(elemAt split 1) (head split == "!")];
# ignore -> regex
substWildcards =
let
special = "^$.+{}()";
escs = "\\*?";
splitString =
let recurse = str : [(substring 0 1 str)] ++
(if str == "" then [] else (recurse (substring 1 (stringLength(str)) str) ));
in str : recurse str;
chars = s: filter (c: c != "" && !isList c) (splitString s);
escape = s: map (c: "\\" + c) (chars s);
in
replaceStrings
((chars special) ++ (escape escs) ++ ["**/" "**" "*" "?"])
((escape special) ++ (escape escs) ++ ["(.*/)?" ".*" "[^/]*" "[^/]"]);
# (regex -> regex) -> regex -> regex
mapAroundCharclass = f: r: # rl = regex or list
let slightFix = replaceStrings ["\\]"] ["]"];
in
concatStringsSep ""
(map (rl: if isList rl then slightFix (elemAt rl 0) else f rl)
(split "(\\[([^\\\\]|\\\\.)+])" r));
# regex -> regex
handleSlashPrefix = l:
let
split = (match "^(/?)(.*)" l);
findSlash = l: if (match ".+/.+" l) != null then "" else l;
hasSlash = mapAroundCharclass findSlash l != l;
in
(if (elemAt split 0) == "/" || hasSlash
then "^"
else "(^|.*/)"
) + (elemAt split 1);
# regex -> regex
handleSlashSuffix = l:
let split = (match "^(.*)/$" l);
in if split != null then (elemAt split 0) + "($|/.*)" else l;
# (regex -> regex) -> [regex, bool] -> [regex, bool]
mapPat = f: l: [(f (head l)) (last l)];
in
map (l: # `l' for "line"
mapPat (l: handleSlashSuffix (handleSlashPrefix (mapAroundCharclass substWildcards l)))
(computeNegation l))
(filter (l: !isList l && !isComment l)
(split "\n" gitignore));
gitignoreFilter = ign: root: filterPattern (gitignoreToPatterns ign) root;
}

88
tests/testdata.nix Normal file
View file

@ -0,0 +1,88 @@
{ pkgs ? import <nixpkgs> {} }:
/*
Programmatically specifies the test data.
*/
let
inherit (pkgs) runCommand;
createTree = ''
touches() { (
mkdir -p "$1"; cd "$1"; shift
touch "$@"
); }
create-tree() { (
mkdir -p "$1"; cd "$1"
touches 1-simpl {1,2,3,4,5,^,$,^$,$^,[,[[,],]],]]],ab,bb,\\,\\\\,simple-test}
touches 1-simpl/1-simpl {1,2,3,4,5,^,$,^$,$^,[,[[,],]],]]],ab,bb,\\,\\\\,simpletest}
touches 1-xxxxx/1-simpl {1,2}
touch {,1-simpl/}char-class-pathalogic
touches 2-negation {.keep,10,20,30,40,50,60,70}
touches 3-wildcards {foo,bar,bbar,baz}.html
touches 3-wildcards/html {foo,bar,bbar,baz}.html
touches 4-escapes {{*,o{,_,__,?,}ther}.html,other.html{,\$,\$\$}}
touches 5-directory {1,2,3,4,5,^,$,^$,$^,[,[[,],]],]]],ab,bb,\\,\\\\}
touches 9-expected {unfiltered,filtered-via-aux-{filter,ignore,filepath}}
); }
create-tree "$1"
cat ${builtins.toFile "nixgitignore-ignores" ignores} > "$1/.gitignore"
cat ${builtins.toFile "nixgitignore-ignores" ignoresAux} > "$1/aux.gitignore"
'';
createTreeRecursive = createTree + "\n" + ''
cp -r "$1" "$1" 2>&1 | grep -vq 'cannot copy a directory, .*into itself' || :
'';
ignores = ''
1-simpl/1
/1-simpl/2
/1-simpl/[35^$[]]
/1-simpl/][\]]
/1-simpl/[^a]b
/1-simpl/[\\]
simple*test
# [^b/]har-class-pathalogic
# this fails, but is pathalogic, so I won't cover it
2-*/[^.]*
!2-*/1?
!2-*/30
!/2-*/70
!/40
!50
3-*/*foo.html
3-*/**/bar.html
4-*/\*.html
4-*/o??ther.html
4-*/o\?ther.html
4-*/other.html$
5-*/
'';
ignoresAux = "/9-expected/*filepath\n";
createSourceTree = createTree: (runCommand "test-tree" {} ''
mkdir -p $out; cd $out;
bash ${builtins.toFile "create-tree" createTree} test-tree
'');
# source is a copy of sourceUnfiltered, which lives in the nix store
sourceUnfiltered = createSourceTree createTree;
sourceUnfilteredRecursive = createSourceTree createTreeRecursive;
in {
inherit sourceUnfiltered sourceUnfilteredRecursive;
}