Skip to content

Commit ec09e19

Browse files
authored
Suggest alternatives when active recipes are not found (#4169)
1 parent f83fd77 commit ec09e19

File tree

2 files changed

+54
-11
lines changed

2 files changed

+54
-11
lines changed

rewrite-core/src/main/java/org/openrewrite/config/Environment.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.openrewrite.config;
1717

18+
import org.apache.commons.lang3.StringUtils;
1819
import org.openrewrite.Contributor;
1920
import org.openrewrite.Recipe;
2021
import org.openrewrite.RecipeException;
@@ -28,7 +29,10 @@
2829
import java.util.*;
2930

3031
import static java.util.Collections.emptyList;
32+
import static java.util.Comparator.comparingInt;
33+
import static java.util.function.Function.identity;
3134
import static java.util.stream.Collectors.toList;
35+
import static java.util.stream.Collectors.toMap;
3236

3337
public class Environment {
3438
private final Collection<? extends ResourceLoader> resourceLoaders;
@@ -134,24 +138,27 @@ public Collection<RecipeDescriptor> listRecipeDescriptors() {
134138
}
135139

136140
public Recipe activateRecipes(Iterable<String> activeRecipes) {
137-
List<Recipe> allRecipes = listRecipes();
141+
Map<String, Recipe> recipesByName = listRecipes().stream().collect(toMap(Recipe::getName, identity()));
138142
List<String> recipesNotFound = new ArrayList<>();
139143
List<Recipe> activatedRecipes = new ArrayList<>();
140144
for (String activeRecipe : activeRecipes) {
141-
boolean foundRecipe = false;
142-
for (Recipe recipe : allRecipes) {
143-
if (activeRecipe.equals(recipe.getName())) {
144-
activatedRecipes.add(recipe);
145-
foundRecipe = true;
146-
break;
147-
}
148-
}
149-
if (!foundRecipe) {
145+
Recipe recipe = recipesByName.get(activeRecipe);
146+
if (recipe == null) {
150147
recipesNotFound.add(activeRecipe);
148+
} else {
149+
activatedRecipes.add(recipe);
151150
}
152151
}
153152
if (!recipesNotFound.isEmpty()) {
154-
throw new RecipeException("Recipes not found: " + String.join(", ", recipesNotFound));
153+
List<String> suggestions = recipesNotFound.stream()
154+
.map(r -> recipesByName.keySet().stream()
155+
.min(comparingInt(a -> StringUtils.getLevenshteinDistance(a, r)))
156+
.orElse(r))
157+
.collect(toList());
158+
String message = String.format("Recipes not found: %s\nDid you mean: %s",
159+
String.join(", ", recipesNotFound),
160+
String.join(", ", suggestions));
161+
throw new RecipeException(message);
155162
}
156163
return new CompositeRecipe(activatedRecipes);
157164
}

rewrite-test/src/test/java/org/openrewrite/config/EnvironmentTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.Properties;
3131

3232
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3334
import static org.openrewrite.test.SourceSpecs.text;
3435

3536
class EnvironmentTest implements RewriteTest {
@@ -100,6 +101,41 @@ void listRecipes() {
100101
assertThat(changes).hasSize(1);
101102
}
102103

104+
@Test
105+
void activeRecipeNotFoundSuggestions() {
106+
var env = Environment.builder()
107+
.load(
108+
new YamlResourceLoader(
109+
//language=yml
110+
new ByteArrayInputStream(
111+
"""
112+
type: specs.openrewrite.org/v1beta/recipe
113+
name: test.ChangeTextToHello
114+
displayName: Change text to hello
115+
recipeList:
116+
- org.openrewrite.text.ChangeText:
117+
toText: Hello
118+
---
119+
type: specs.openrewrite.org/v1beta/recipe
120+
name: test.ChangeTextToHelloWorld
121+
displayName: Change text to hello world
122+
recipeList:
123+
- org.openrewrite.text.ChangeText:
124+
toText: Hello
125+
""".getBytes()
126+
),
127+
URI.create("rewrite.yml"),
128+
new Properties()
129+
)
130+
)
131+
.build();
132+
133+
assertThatExceptionOfType(RecipeException.class)
134+
.isThrownBy(() -> env.activateRecipes("foo.ChangeTextToHelloWorld"))
135+
.withMessageContaining("foo.ChangeTextToHelloWorld")
136+
.withMessageContaining("test.ChangeTextToHelloWorld");
137+
}
138+
103139
@Test
104140
void recipeWithoutRequiredConfiguration() {
105141
var env = Environment.builder()

0 commit comments

Comments
 (0)