diff --git a/crate2nix/templates/nix/crate2nix/tests/default.nix b/crate2nix/templates/nix/crate2nix/tests/default.nix index ec26601d..12c08c82 100644 --- a/crate2nix/templates/nix/crate2nix/tests/default.nix +++ b/crate2nix/templates/nix/crate2nix/tests/default.nix @@ -8,6 +8,7 @@ let "enableFeatures" "expandFeatures" "packageFeatures" + "pathsFromPathPattern" ]; testsInFile = f: let diff --git a/crate2nix/templates/nix/crate2nix/tests/pathsFromPathPattern.nix b/crate2nix/templates/nix/crate2nix/tests/pathsFromPathPattern.nix new file mode 100644 index 00000000..fc1f30c7 --- /dev/null +++ b/crate2nix/templates/nix/crate2nix/tests/pathsFromPathPattern.nix @@ -0,0 +1,55 @@ +{ lib, ... }: +let + module = import ../../../../../nix/lib/paths-from-path-pattern.nix { inherit lib; }; + pathToRegex = module.pathToRegex; + pathsFromPathPattern = module.pathsFromPathPattern; +in +{ + testPathsFromPathPattern = { + expr = pathsFromPathPattern ./. "p*sFro[l-p]PathP?tter[^x].{nix,xin}"; + expected = [ "pathsFromPathPattern.nix" ]; + }; + testGlobStar = { + expr = [ + (pathToRegex "crates/*") + (builtins.match (pathToRegex "crates/*") "crates/main" != null) + ]; + expected = [ "crates[/][^/]*" true ]; + }; + testGlobDoubleStar = { + expr = [ + (pathToRegex "crates/**/*") + (builtins.match (pathToRegex "crates/**/*") "crates/prod/main" != null) + ]; + expected = [ "crates[/].*[/][^/]*" true ]; + }; + testGlobQuestionMark = { + expr = [ + (pathToRegex "crate?/*") + (builtins.match (pathToRegex "crate?/*") "crates/main" != null) + ]; + expected = [ "crate[^/][/][^/]*" true ]; + }; + testGlobAlternative = { + expr = [ + (pathToRegex "Cargo.{toml,lock}") + (builtins.match (pathToRegex "Cargo.{toml,lock}") "Cargo.toml" != null) + ]; + expected = [ "Cargo[.](toml|lock)" true ]; + }; + testGlobRange = { + expr = [ + (pathToRegex "artifacts/v[0-9].tgz") + (builtins.match (pathToRegex "artifacts/v[0-9].tgz") "artifacts/v3.tgz" != null) + ]; + expected = [ "artifacts[/]v[0-9][.]tgz" true ]; + }; + testGlobCaretNegation = { + expr = [ + (pathToRegex "h[^w-x]") + (builtins.match (pathToRegex "h[^w-x]") "hi" != null) + (builtins.match (pathToRegex "h[^w-x]") "hx" != null) + ]; + expected = [ "h[^w-x]" true false ]; + }; +} diff --git a/nix/lib/paths-from-path-pattern.nix b/nix/lib/paths-from-path-pattern.nix new file mode 100644 index 00000000..499a33de --- /dev/null +++ b/nix/lib/paths-from-path-pattern.nix @@ -0,0 +1,59 @@ +{ lib }: +rec { + /** + Converts a file path with wildcards to a regular expression pattern. + Handles common wildcards: + + * - matches any number of characters except / + ** - matches any number of characters including / + ? - matches exactly one character except / + {a,b,c} - matches any one of the patterns separated by commas + [abc] - matches any character in the brackets + [0-9] - matches any character in the brackets + [!abc] or [^abc] - matches any character not in the brackets, or in the given range + + Example: + pathToRegex "crates/*" -> "crates/[^/]*" + */ + pathToRegex = path: + let + # Escape special regex characters except characters used in globs: * ? ^ [ ] { } + escapeRegex = str: + lib.replaceStrings + [ "." "+" "$" "/" "(" ")" "|" "\\" ] + [ "[.]" "[+]" "[$]" "[/]" "[(]" "[)]" "[|]" "[\\]" ] + str; + globToRegex = lib.replaceStrings + [ "**" "*" "?" "," "{" "}" ] + [ ".*" "[^/]*" "[^/]" "|" "(" ")" ]; + in + globToRegex (escapeRegex path); + + /** + Given a pathPattern that may contain waildcards, find all files and + directories relative to dir that match the pattern. + + Example: + ppathsFromPathPattern "crates/*" src -> [ "crates/app" "crates/lib" ] + */ + pathsFromPathPattern = dir: pathPattern: + let + regex = pathToRegex pathPattern; + helper = subdir: lib.flatten + (lib.mapAttrsToList + (name: type: + let path = if subdir == null then name else subdir + "/${name}"; in + if builtins.match regex path != null then + [ path ] + else if type == "directory" then + helper path + else + [ ] + ) + (builtins.readDir (if + subdir == null then + dir else dir + "/${subdir}")) + ); + in + helper null; +} diff --git a/tools.nix b/tools.nix index 3a867d4d..ca3a5e87 100644 --- a/tools.nix +++ b/tools.nix @@ -13,6 +13,7 @@ let cargoNix = pkgs.callPackage ./crate2nix/Cargo.nix { inherit strictDeprecation; }; crate2nix = cargoNix.rootCrate.build; + pathsFromPathPattern = (pkgs.callPackage ./nix/lib/paths-from-path-pattern.nix { }).pathsFromPathPattern; in rec { @@ -406,7 +407,8 @@ rec { rootCargo = builtins.fromTOML (builtins.readFile "${src}/Cargo.toml"); isWorkspace = rootCargo ? "workspace"; isPackage = rootCargo ? "package"; - containedCrates = rootCargo.workspace.members ++ (if isPackage then [ "." ] else [ ]); + containedCrates = lib.flatten (builtins.map (pathsFromPathPattern src) rootCargo.workspace.members) + ++ (if isPackage then [ "." ] else [ ]); getCrateNameFromPath = path: let @@ -436,3 +438,4 @@ rec { }; }; } +