From 741a6bfad53d1efbc34fb148c8ca4b9dc01691d4 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 22:27:16 +0100 Subject: [PATCH 1/9] profile: allow different types of matchers --- src/nix/profile.cc | 164 ++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 75 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index fc669d5ed..41dcccc50 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -453,55 +453,86 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; -class MixProfileElementMatchers : virtual Args +enum MatcherType { - std::vector _matchers; + Regex, + StorePath, +}; + +struct Matcher +{ + MatcherType type; + std::string title; + std::function matches; +}; + +Matcher createRegexMatcher(const std::string & pattern) +{ + std::regex reg(pattern, std::regex::extended | std::regex::icase); + return { + .type = MatcherType::Regex, + .title = fmt("Regex '%s'", pattern), + .matches = [reg](const std::string &name, const ProfileElement & element) { + return std::regex_match(element.identifier(), reg); + }, + }; +} + +Matcher createStorePathMatcher(const nix::StorePath & storePath) +{ + return { + .type = MatcherType::StorePath, + .title = fmt("Store path '%s'", storePath.to_string()), + .matches = [storePath](const std::string &name, const ProfileElement & element) { + return element.storePaths.count(storePath); + } + }; +} + +class MixProfileElementMatchers : virtual Args, virtual StoreCommand +{ + std::vector _matchers; public: MixProfileElementMatchers() { - expectArgs("elements", &_matchers); + expectArgs(ExpectedArg { + .label = "elements", + .optional = true, + .handler = {[this](std::vector args) { + for (auto & arg : args) { + if (auto n = string2Int(arg)) { + throw Error("'nix profile' no longer supports indices ('%d')", *n); + } else if (getStore()->isStorePath(arg)) { + _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); + } else { + _matchers.push_back(createRegexMatcher(arg)); + } + } + }} + }); } - struct RegexPattern { - std::string pattern; - std::regex reg; - }; - typedef std::variant Matcher; - - std::vector getMatchers(ref store) - { - std::vector res; - - for (auto & s : _matchers) { - if (auto n = string2Int(s)) - throw Error("'nix profile' no longer supports indices ('%d')", *n); - else if (store->isStorePath(s)) - res.push_back(s); - else - res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)}); + std::set getMatchingElementNames(ProfileManifest & manifest) { + if (_matchers.empty()) { + throw UsageError("No packages specified."); } - return res; - } - - bool matches( - const Store & store, - const std::string & name, - const ProfileElement & element, - const std::vector & matchers) - { - for (auto & matcher : matchers) { - if (auto path = std::get_if(&matcher)) { - if (element.storePaths.count(store.parseStorePath(*path))) return true; - } else if (auto regex = std::get_if(&matcher)) { - if (std::regex_match(name, regex->reg)) - return true; + std::set result; + for (auto & matcher : _matchers) { + bool foundMatch = false; + for (auto & [name, element] : manifest.elements) { + if (matcher.matches(name, element)) { + result.insert(name); + foundMatch = true; + } + } + if (!foundMatch) { + warn("%s does not match any packages in the profile.", matcher.title); } } - - return false; + return result; } }; @@ -523,16 +554,19 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem { ProfileManifest oldManifest(*getEvalState(), *profile); - auto matchers = getMatchers(store); + ProfileManifest newManifest = oldManifest; - ProfileManifest newManifest; + auto matchingElementNames = getMatchingElementNames(oldManifest); - for (auto & [name, element] : oldManifest.elements) { - if (!matches(*store, name, element, matchers)) { - newManifest.elements.insert_or_assign(name, std::move(element)); - } else { - notice("removing '%s'", element.identifier()); - } + if (matchingElementNames.empty()) { + warn ("No packages to remove. Use 'nix profile list' to see the current profile."); + return; + } + + for (auto & name : matchingElementNames) { + auto & element = oldManifest.elements[name]; + notice("removing '%s'", element.identifier()); + newManifest.elements.erase(name); } auto removedCount = oldManifest.elements.size() - newManifest.elements.size(); @@ -540,16 +574,6 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem removedCount, newManifest.elements.size()); - if (removedCount == 0) { - for (auto matcher: matchers) { - if (const Path * path = std::get_if(&matcher)) { - warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)) { - warn("'%s' does not match any packages", regex->pattern); - } - } - warn ("Use 'nix profile list' to see the current profile."); - } updateProfile(newManifest.build(store)); } }; @@ -572,20 +596,20 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf { ProfileManifest manifest(*getEvalState(), *profile); - auto matchers = getMatchers(store); - Installables installables; std::vector elems; - auto matchedCount = 0; auto upgradedCount = 0; - for (auto & [name, element] : manifest.elements) { - if (!matches(*store, name, element, matchers)) { - continue; - } + auto matchingElementNames = getMatchingElementNames(manifest); - matchedCount++; + if (matchingElementNames.empty()) { + warn("No packages to upgrade. Use 'nix profile list' to see the current profile."); + return; + } + + for (auto & name : matchingElementNames) { + auto & element = manifest.elements[name]; if (!element.source) { warn( @@ -641,18 +665,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf } if (upgradedCount == 0) { - if (matchedCount == 0) { - for (auto & matcher : matchers) { - if (const Path * path = std::get_if(&matcher)) { - warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)) { - warn("'%s' does not match any packages", regex->pattern); - } - } - } else { - warn("Found some packages but none of them could be upgraded."); - } - warn ("Use 'nix profile list' to see the current profile."); + warn("Found some packages but none of them could be upgraded."); + return; } auto builtPaths = builtPathsPerInstallable( From d6f5da51d3ae11c6771c68ebb65e7a560af167b5 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 22:33:37 +0100 Subject: [PATCH 2/9] profile: match on package name instead of regex --- src/nix/profile.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 41dcccc50..d79f1158b 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -457,6 +457,7 @@ enum MatcherType { Regex, StorePath, + Name, }; struct Matcher @@ -489,6 +490,16 @@ Matcher createStorePathMatcher(const nix::StorePath & storePath) }; } +Matcher createNameMatcher(const std::string & name) { + return { + .type = MatcherType::Name, + .title = fmt("Package name '%s'", name), + .matches = [name](const std::string &elementName, const ProfileElement & element) { + return elementName == name; + } + }; +} + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { std::vector _matchers; @@ -507,7 +518,7 @@ public: } else if (getStore()->isStorePath(arg)) { _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); } else { - _matchers.push_back(createRegexMatcher(arg)); + _matchers.push_back(createNameMatcher(arg)); } } }} From 87741dbd2118b0c90db6a37525b36c7bc93617ce Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 20:33:59 +0100 Subject: [PATCH 3/9] profile: add --regex option to match packages --- src/nix/profile-remove.md | 2 +- src/nix/profile-upgrade.md | 2 +- src/nix/profile.cc | 10 +++++++++- tests/functional/nix-profile.sh | 10 ++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md index 1f6532250..e2dea3389 100644 --- a/src/nix/profile-remove.md +++ b/src/nix/profile-remove.md @@ -11,7 +11,7 @@ R""( * Remove all packages: ```console - # nix profile remove '.*' + # nix profile remove --regex '.*' ``` * Remove a package by store path: diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md index 432b8fa94..e04ad109e 100644 --- a/src/nix/profile-upgrade.md +++ b/src/nix/profile-upgrade.md @@ -6,7 +6,7 @@ R""( reference: ```console - # nix profile upgrade '.*' + # nix profile upgrade --regex '.*' ``` * Upgrade a specific package by name: diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d79f1158b..c08d02e70 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -508,7 +508,15 @@ public: MixProfileElementMatchers() { - expectArgs(ExpectedArg { + addFlag({ + .longName = "regex", + .description = "A regular expression to match one or more packages in the profile.", + .labels = {"pattern"}, + .handler = {[this](std::string arg) { + _matchers.push_back(createRegexMatcher(arg)); + }}, + }); + expectArgs({ .label = "elements", .optional = true, .handler = {[this](std::vector args) { diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 88b713d53..274b72de2 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -71,6 +71,16 @@ nix profile upgrade flake1 [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man" +# Test upgrading package using regular expression. +printf 2.1 > $flake1Dir/version +nix profile upgrade --regex '.*' +[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] +nix profile rollback + +# Test removing all packages using regular expression. +nix profile remove --regex '.*' 2>&1 | grep "removed 2 packages, kept 0 packages" +nix profile rollback + # Test 'history', 'diff-closures'. nix profile diff-closures From 9fac62435c5f05783456512b09e3f207a9d62004 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 23:35:10 +0100 Subject: [PATCH 4/9] tests/functional: add assertStderr function Currently there isn't a convenient way to check for multiline output. In addition, these outputs will easily change and having a diff between the expected an the actual output upon failures is convenient. --- tests/functional/common/vars-and-functions.sh.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in index 8fef29f97..3975986c0 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -216,6 +216,17 @@ expectStderr() { return 0 } +# Run a command and check whether the stderr matches stdin. +# Show a diff when output does not match. +# Usage: +# +# assertStderr nix profile remove nothing << EOF +# error: This error is expected +# EOF +assertStderr() { + diff -u /dev/stdin <($@ 2>/dev/null 2>&1) +} + needLocalStore() { if [[ "$NIX_REMOTE" == "daemon" ]]; then skipTest "Can’t run through the daemon ($1)" From fb391ebc77cc02d74eae4b4826137ed0d79b0455 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 23:39:45 +0100 Subject: [PATCH 5/9] profile: add tests for not matching any packages --- tests/functional/nix-profile.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 274b72de2..67c8bcc98 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -77,6 +77,18 @@ nix profile upgrade --regex '.*' [[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] nix profile rollback +# Test matching no packages using literal package name. +assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF +warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. +warning: No packages to upgrade. Use 'nix profile list' to see the current profile. +EOF + +# Test matching no packages using regular expression. +assertStderr nix --offline profile upgrade --regex '.*unknown_package.*' << EOF +warning: Regex '.*unknown_package.*' does not match any packages in the profile. +warning: No packages to upgrade. Use 'nix profile list' to see the current profile. +EOF + # Test removing all packages using regular expression. nix profile remove --regex '.*' 2>&1 | grep "removed 2 packages, kept 0 packages" nix profile rollback @@ -85,6 +97,10 @@ nix profile rollback nix profile diff-closures # Test rollback. +printf World > $flake1Dir/who +nix profile upgrade flake1 +printf NixOS > $flake1Dir/who +nix profile upgrade flake1 nix profile rollback [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] From 7a4d5e89d33de9d2c656a3d5b4fd44d9cf2cb05d Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 6 Mar 2024 22:04:53 +0100 Subject: [PATCH 6/9] profile: add --all option to match any package --- src/nix/profile-remove.md | 9 ++++++++- src/nix/profile-upgrade.md | 8 +++++++- src/nix/profile.cc | 16 ++++++++++++++++ tests/functional/nix-profile.sh | 7 +++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md index e2dea3389..e7e5e0dfb 100644 --- a/src/nix/profile-remove.md +++ b/src/nix/profile-remove.md @@ -11,9 +11,16 @@ R""( * Remove all packages: ```console - # nix profile remove --regex '.*' + # nix profile remove --all ``` +* Remove packages by regular expression: + + ```console + # nix profile remove --regex '.*vim.*' + ``` + + * Remove a package by store path: ```console diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md index e04ad109e..da7a668db 100644 --- a/src/nix/profile-upgrade.md +++ b/src/nix/profile-upgrade.md @@ -6,7 +6,7 @@ R""( reference: ```console - # nix profile upgrade --regex '.*' + # nix profile upgrade --all ``` * Upgrade a specific package by name: @@ -15,6 +15,12 @@ R""( # nix profile upgrade hello ``` +* Upgrade all packages that include 'vim' in their name: + + ```console + # nix profile upgrade --regex '.*vim.*' + ``` + # Description This command upgrades a previously installed package in a Nix profile, diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c08d02e70..701c5cb29 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -458,6 +458,7 @@ enum MatcherType Regex, StorePath, Name, + All, }; struct Matcher @@ -500,6 +501,14 @@ Matcher createNameMatcher(const std::string & name) { }; } +Matcher all = { + .type = MatcherType::All, + .title = "--all", + .matches = [](const std::string &name, const ProfileElement & element) { + return true; + } +}; + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { std::vector _matchers; @@ -508,6 +517,13 @@ public: MixProfileElementMatchers() { + addFlag({ + .longName = "all", + .description = "Match all packages in the profile.", + .handler = {[this]() { + _matchers.push_back(all); + }}, + }); addFlag({ .longName = "regex", .description = "A regular expression to match one or more packages in the profile.", diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 67c8bcc98..b8513ac02 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -77,6 +77,13 @@ nix profile upgrade --regex '.*' [[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] nix profile rollback +# Test upgrading all packages +printf 2.2 > $flake1Dir/version +nix profile upgrade --all +[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.2/bin/hello ]] +nix profile rollback +printf 1.0 > $flake1Dir/version + # Test matching no packages using literal package name. assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. From 91f068c19309091b85f18fb5fc10ab3644642d50 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 6 Mar 2024 22:46:47 +0100 Subject: [PATCH 7/9] profile: make --all exclusive --- src/nix/profile.cc | 9 +++++++++ tests/functional/nix-profile.sh | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 701c5cb29..d9455b4ee 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -554,6 +554,15 @@ public: throw UsageError("No packages specified."); } + if (std::find_if(_matchers.begin(), _matchers.end(), [](const Matcher & m) { return m.type == MatcherType::All; }) != _matchers.end() && _matchers.size() > 1) { + throw UsageError("--all cannot be used with package names or regular expressions."); + } + + if (manifest.elements.empty()) { + warn("There are no packages in the profile."); + return {}; + } + std::set result; for (auto & matcher : _matchers) { bool foundMatch = false; diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index b8513ac02..58fdce411 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -84,6 +84,12 @@ nix profile upgrade --all nix profile rollback printf 1.0 > $flake1Dir/version +# Test --all exclusivity. +assertStderr nix --offline profile upgrade --all foo << EOF +error: --all cannot be used with package names or regular expressions. +Try 'nix --help' for more information. +EOF + # Test matching no packages using literal package name. assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. From 4741d3e308a716e5637af357237e1f44c7d598b6 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 00:21:40 +0100 Subject: [PATCH 8/9] add release note --- doc/manual/rl-next/profile-regex-all.md | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 doc/manual/rl-next/profile-regex-all.md diff --git a/doc/manual/rl-next/profile-regex-all.md b/doc/manual/rl-next/profile-regex-all.md new file mode 100644 index 000000000..e3e6849cc --- /dev/null +++ b/doc/manual/rl-next/profile-regex-all.md @@ -0,0 +1,35 @@ +--- +synopsis: Introduction of `--regex` and `--all` in `nix profile remove` and `nix profile upgrade` +prs: 10166 +--- + +Previously the command-line arguments for `nix profile remove` and `nix profile upgrade` matched the package entries using regular expression. +For instance: + +``` +nix profile remove '.*vim.*' +``` + +This would remove all packages that contain `vim` in their name. + +In most cases, only singular package names were used to remove and upgrade packages. Mixing this with regular expressions sometimes lead to unintended behavior. For instance, `python3.1` could match `python311`. + +To avoid unintended behavior, the arguments are now only matching exact names. + +Matching using regular expressions is still possible by using the new `--regex` flag: + +``` +nix profile remove --regex '.*vim.*' +``` + +One of the most useful cases for using regular expressions was to upgrade all packages. This was previously accomplished by: + +``` +nix profile upgrade '.*' +``` + +With the introduction of the `--all` flag, this now becomes more straightforward: + +``` +nix profile upgrade --all +``` From 3d628d17041bbaab745dd04bedb2cea21c1f11a5 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 21:40:18 +0100 Subject: [PATCH 9/9] profile: convert Matcher to abstract class --- src/nix/profile.cc | 122 ++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d9455b4ee..75f22934f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -453,65 +453,85 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; -enum MatcherType -{ - Regex, - StorePath, - Name, - All, -}; - struct Matcher { - MatcherType type; - std::string title; - std::function matches; + virtual std::string getTitle() = 0; + virtual bool matches(const std::string & name, const ProfileElement & element) = 0; }; -Matcher createRegexMatcher(const std::string & pattern) +struct RegexMatcher : public Matcher { - std::regex reg(pattern, std::regex::extended | std::regex::icase); - return { - .type = MatcherType::Regex, - .title = fmt("Regex '%s'", pattern), - .matches = [reg](const std::string &name, const ProfileElement & element) { - return std::regex_match(element.identifier(), reg); - }, - }; -} + std::regex regex; + std::string pattern; -Matcher createStorePathMatcher(const nix::StorePath & storePath) + RegexMatcher(const std::string & pattern) : regex(pattern, std::regex::extended | std::regex::icase), pattern(pattern) + { } + + std::string getTitle() override + { + return fmt("Regex '%s'", pattern); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return std::regex_match(element.identifier(), regex); + } +}; + +struct StorePathMatcher : public Matcher { - return { - .type = MatcherType::StorePath, - .title = fmt("Store path '%s'", storePath.to_string()), - .matches = [storePath](const std::string &name, const ProfileElement & element) { - return element.storePaths.count(storePath); - } - }; -} + nix::StorePath storePath; -Matcher createNameMatcher(const std::string & name) { - return { - .type = MatcherType::Name, - .title = fmt("Package name '%s'", name), - .matches = [name](const std::string &elementName, const ProfileElement & element) { - return elementName == name; - } - }; -} + StorePathMatcher(const nix::StorePath & storePath) : storePath(storePath) + { } -Matcher all = { - .type = MatcherType::All, - .title = "--all", - .matches = [](const std::string &name, const ProfileElement & element) { + std::string getTitle() override + { + return fmt("Store path '%s'", storePath.to_string()); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return element.storePaths.count(storePath); + } +}; + +struct NameMatcher : public Matcher +{ + std::string name; + + NameMatcher(const std::string & name) : name(name) + { } + + std::string getTitle() override + { + return fmt("Package name '%s'", name); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return name == this->name; + } +}; + +struct AllMatcher : public Matcher +{ + std::string getTitle() override + { + return "--all"; + } + + bool matches(const std::string & name, const ProfileElement & element) override + { return true; } }; +AllMatcher all; + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { - std::vector _matchers; + std::vector> _matchers; public: @@ -521,7 +541,7 @@ public: .longName = "all", .description = "Match all packages in the profile.", .handler = {[this]() { - _matchers.push_back(all); + _matchers.push_back(ref(std::shared_ptr(&all, [](AllMatcher*) {}))); }}, }); addFlag({ @@ -529,7 +549,7 @@ public: .description = "A regular expression to match one or more packages in the profile.", .labels = {"pattern"}, .handler = {[this](std::string arg) { - _matchers.push_back(createRegexMatcher(arg)); + _matchers.push_back(make_ref(arg)); }}, }); expectArgs({ @@ -540,9 +560,9 @@ public: if (auto n = string2Int(arg)) { throw Error("'nix profile' no longer supports indices ('%d')", *n); } else if (getStore()->isStorePath(arg)) { - _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); + _matchers.push_back(make_ref(getStore()->parseStorePath(arg))); } else { - _matchers.push_back(createNameMatcher(arg)); + _matchers.push_back(make_ref(arg)); } } }} @@ -554,7 +574,7 @@ public: throw UsageError("No packages specified."); } - if (std::find_if(_matchers.begin(), _matchers.end(), [](const Matcher & m) { return m.type == MatcherType::All; }) != _matchers.end() && _matchers.size() > 1) { + if (std::find_if(_matchers.begin(), _matchers.end(), [](const ref & m) { return m.dynamic_pointer_cast(); }) != _matchers.end() && _matchers.size() > 1) { throw UsageError("--all cannot be used with package names or regular expressions."); } @@ -567,13 +587,13 @@ public: for (auto & matcher : _matchers) { bool foundMatch = false; for (auto & [name, element] : manifest.elements) { - if (matcher.matches(name, element)) { + if (matcher->matches(name, element)) { result.insert(name); foundMatch = true; } } if (!foundMatch) { - warn("%s does not match any packages in the profile.", matcher.title); + warn("%s does not match any packages in the profile.", matcher->getTitle()); } } return result;