Skip to content

Commit e0a0902

Browse files
authored
cf: allow for trailing slash on CF modpack page URL (#430)
1 parent 01b3c38 commit e0a0902

4 files changed

Lines changed: 105 additions & 17 deletions

File tree

src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
import java.util.List;
1111
import java.util.Set;
1212
import java.util.concurrent.Callable;
13-
import java.util.regex.Matcher;
14-
import java.util.regex.Pattern;
1513
import java.util.stream.Collectors;
1614
import java.util.stream.Stream;
1715
import me.itzg.helpers.McImageHelper;
16+
import me.itzg.helpers.curseforge.ModpacksPageUrlParser.Parsed;
1817
import me.itzg.helpers.files.ResultsFileWriter;
1918
import me.itzg.helpers.files.TabularOutput;
2019
import me.itzg.helpers.http.PathOrUri;
@@ -161,27 +160,15 @@ static class Listed {
161160
@Option(names = "--missing-mods-filename", defaultValue = "MODS_NEED_DOWNLOAD.txt")
162161
String missingModsFilename;
163162

164-
private static final Pattern PAGE_URL_PATTERN = Pattern.compile(
165-
"https://(www|beta)\\.curseforge\\.com/minecraft/modpacks/(?<slug>.+?)(/(files|download)(/(?<fileId>\\d+)?)?)?");
166-
167163
@Override
168164
public Integer call() throws Exception {
169165
// https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files
170166
// https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files/4248390
171167

172168
if (pageUrl != null) {
173-
final Matcher m = PAGE_URL_PATTERN.matcher(pageUrl);
174-
if (m.matches()) {
175-
slug = m.group("slug");
176-
final String fileIdStr = m.group("fileId");
177-
if (fileIdStr != null) {
178-
fileId = Integer.parseInt(fileIdStr);
179-
}
180-
}
181-
else {
182-
System.err.println("Unexpected URL structure: "+pageUrl);
183-
return ExitCode.USAGE;
184-
}
169+
final Parsed parsed = ModpacksPageUrlParser.parse(pageUrl);
170+
slug = parsed.getSlug();
171+
fileId = parsed.getFileId();
185172
}
186173

187174
if (slug == null) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package me.itzg.helpers.curseforge;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import me.itzg.helpers.errors.InvalidParameterException;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
class ModpacksPageUrlParser {
11+
private static final Pattern PAGE_URL_PATTERN = Pattern.compile(
12+
"https://(www|beta)\\.curseforge\\.com/minecraft/modpacks/(?<slug>[^/]+?)(/((files|download)(/(?<fileId>\\d+)?)?)?)?");
13+
14+
@Data @Builder
15+
public static class Parsed {
16+
String slug;
17+
Integer fileId;
18+
}
19+
20+
@NotNull
21+
public static Parsed parse(String pageUrl) {
22+
if (pageUrl == null) {
23+
return Parsed.builder().build();
24+
}
25+
26+
final Matcher m = PAGE_URL_PATTERN.matcher(pageUrl);
27+
if (m.matches()) {
28+
final String slug = m.group("slug");
29+
final String fileIdStr = m.group("fileId");
30+
if (fileIdStr != null) {
31+
return Parsed.builder()
32+
.slug(slug)
33+
.fileId(Integer.parseInt(fileIdStr))
34+
.build();
35+
}
36+
else {
37+
return Parsed.builder()
38+
.slug(slug)
39+
.build();
40+
}
41+
}
42+
else {
43+
throw new InvalidParameterException("Unexpected CF page URL structure: " + pageUrl);
44+
}
45+
46+
47+
}
48+
}

src/main/java/me/itzg/helpers/paper/PaperDownloadsClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import me.itzg.helpers.paper.model.VersionInfo;
1313
import reactor.core.publisher.Mono;
1414

15+
/**
16+
* <a href="https://api.papermc.io/docs/swagger-ui/index.html?configUrl=/openapi/swagger-config">Downloads API</a>
17+
*/
1518
public class PaperDownloadsClient implements AutoCloseable{
1619

1720
private final UriBuilder uriBuilder;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package me.itzg.helpers.curseforge;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
6+
import me.itzg.helpers.curseforge.ModpacksPageUrlParser.Parsed;
7+
import me.itzg.helpers.errors.InvalidParameterException;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.ValueSource;
10+
11+
class ModpacksPageUrlParserTest {
12+
13+
@ParameterizedTest
14+
@ValueSource(strings = {
15+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world",
16+
"https://beta.curseforge.com/minecraft/modpacks/create-perfect-world",
17+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/",
18+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/files",
19+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/files/",
20+
})
21+
void justSlug(String url) {
22+
final Parsed parsed = ModpacksPageUrlParser.parse(url);
23+
assertThat(parsed).isNotNull();
24+
assertThat(parsed.getSlug()).isEqualTo("create-perfect-world");
25+
assertThat(parsed.getFileId()).isNull();
26+
}
27+
28+
@ParameterizedTest
29+
@ValueSource(strings = {
30+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/files/5181367"
31+
})
32+
void slugAndFileId(String url) {
33+
final Parsed parsed = ModpacksPageUrlParser.parse(url);
34+
assertThat(parsed).isNotNull();
35+
assertThat(parsed.getSlug()).isEqualTo("create-perfect-world");
36+
assertThat(parsed.getFileId()).isEqualTo(5181367);
37+
}
38+
39+
@ParameterizedTest
40+
@ValueSource(strings = {
41+
"",
42+
"https://www.google.com",
43+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/wrong/",
44+
"https://www.curseforge.com/minecraft/modpacks/create-perfect-world/invalid/5181367"
45+
})
46+
void invalid(String url) {
47+
assertThatThrownBy(() -> ModpacksPageUrlParser.parse(url))
48+
.isInstanceOf(InvalidParameterException.class);
49+
}
50+
}

0 commit comments

Comments
 (0)