commit c2b3bc4515d7f62e0a86cd01b2ee9652981447cd Author: kageru Date: Fri Jul 3 13:05:23 2020 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f2a078 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.gradle/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..71a640c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# 能率 +I’m trying to prove a point by implementing something in Java. +More details will follow on [my blog](https://ruru.moe). diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..4a9f046 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + java + application +} + +group = "moe.kageru" +version = "0.1.0" + +repositories { + mavenCentral() +} + +java { + sourceCompatibility = JavaVersion.VERSION_15 + targetCompatibility = JavaVersion.VERSION_15 +} + +tasks.withType { + options.compilerArgs.add("--enable-preview") +} + +tasks.run { +} + +tasks.test { + useJUnitPlatform() + jvmArgs("--enable-preview") +} + +application { + mainClass.set("nouritsu.Nouritsu") + applicationDefaultJvmArgs = listOf("--enable-preview") +} + +dependencies { + implementation("org.nanohttpd:nanohttpd:2.3.1") + implementation("io.vavr:vavr:1.0.0-alpha-3") + testImplementation("junit", "junit", "4.12") +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..7a7af96 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "nouritsu" + diff --git a/src/main/java/nouritsu/Dao.java b/src/main/java/nouritsu/Dao.java new file mode 100644 index 0000000..c6b4821 --- /dev/null +++ b/src/main/java/nouritsu/Dao.java @@ -0,0 +1,18 @@ +package nouritsu; + +import io.vavr.collection.List; +import io.vavr.control.Either; +import nouritsu.types.Message; + +import java.util.stream.Collectors; + +public class Dao { + public Either clearList(String id) { + return Either.right("Cleared list of user “%s“".formatted(id)); + } + + public Either getList(String id) { + return Either.right(List.of("first", "second", "third") + .collect(Collectors.joining(","))); + } +} diff --git a/src/main/java/nouritsu/Nouritsu.java b/src/main/java/nouritsu/Nouritsu.java new file mode 100644 index 0000000..b5653fd --- /dev/null +++ b/src/main/java/nouritsu/Nouritsu.java @@ -0,0 +1,102 @@ +package nouritsu; + +import fi.iki.elonen.NanoHTTPD; +import fi.iki.elonen.NanoHTTPD.Response.Status; +import io.vavr.Tuple2; +import io.vavr.collection.HashMap; +import io.vavr.collection.List; +import io.vavr.collection.Map; +import io.vavr.control.Either; +import io.vavr.control.Try; +import nouritsu.types.Message; + +import java.io.IOException; +import java.util.function.Function; + +public class Nouritsu extends NanoHTTPD { + private final Dao dao; + + public Nouritsu(int port, Dao dao) throws IOException { + super(port); + start(10_000, false); + this.dao = dao; + } + + @Override + public Response serve(IHTTPSession session) { + return Try.of(() -> processRequest(session, dao)) + .fold( + err -> newFixedLengthResponse(Status.INTERNAL_ERROR, MIME_PLAINTEXT, err.getMessage()), + response -> newFixedLengthResponse(response.status(), MIME_PLAINTEXT, response.text()) + ); + } + + private static final String HELP = """ + Valid endpoints are (not case sensitive): + /addStore/?name=storeName§ions=first,second,third + /addItem/?name=itemName&category=itemCategory + /addToList/?user=userId&itemName + /getList/?user=userId&store=optionalStoreName + /clearList/?user=userId + """; + + private static Message processRequest(IHTTPSession session, Dao dao) { + var params = extractParams(session); + return switch (session.getUri().replaceAll("/", "").toLowerCase()) { + case "", "index.html" -> new Message(HELP, Status.OK); + case "addstore" -> addStore(params, dao); + case "additem" -> addItem(params, dao); + case "addtolist" -> addToList(params, dao); + case "getlist" -> getList(params, dao); + case "clearlist" -> clearList(params, dao); + default -> new Message(HELP, Status.NOT_FOUND); + }; + } + + // Repack the parameters into vavr collections. + private static Map> extractParams(IHTTPSession session) { + return session.getParameters() + .entrySet() + .stream() + .map(e -> new Tuple2<>(e.getKey(), List.ofAll(e.getValue()))) + .collect(HashMap.collector()); + } + + private static Message clearList(Map> params, Dao dao) { + return getFromParams(params, "user") + .map(List::head) // save, the list can’t be empty + .flatMap(dao::clearList) + .fold(Function.identity(), Message.OK); + } + + private static Message getList(Map> params, Dao dao) { + return getFromParams(params, "user") + .map(List::head) // save, the list can’t be empty + .flatMap(dao::getList) + .fold(Function.identity(), Message.OK); + } + + private static Message addToList(Map> params, Dao dao) { + return new Message("", Status.NOT_IMPLEMENTED); + } + + private static Message addItem(Map> params, Dao dao) { + return new Message("", Status.NOT_IMPLEMENTED); + } + + private static Message addStore(Map> params, Dao dao) { + return new Message("", Status.NOT_IMPLEMENTED); + } + + private static Either> getFromParams(Map> params, String key) { + return params.get(key).toEither(new Message("Parameter “%s” not found".formatted(key), Status.NOT_FOUND)); + } + + public static void main(String[] args) { + try { + new Nouritsu(14523, new Dao()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/nouritsu/types/Category.java b/src/main/java/nouritsu/types/Category.java new file mode 100644 index 0000000..a285247 --- /dev/null +++ b/src/main/java/nouritsu/types/Category.java @@ -0,0 +1,12 @@ +package nouritsu.types; + +public enum Category { + CANS, + FRUITS, + VEGETABLES, + PASTA, + RICE, + MEAT, + DAIRY, + HYGIENE, +} diff --git a/src/main/java/nouritsu/types/Message.java b/src/main/java/nouritsu/types/Message.java new file mode 100644 index 0000000..4398c9a --- /dev/null +++ b/src/main/java/nouritsu/types/Message.java @@ -0,0 +1,11 @@ +package nouritsu.types; + +import fi.iki.elonen.NanoHTTPD.Response.Status; +import io.vavr.Function1; + +public record Message(String text, Status status) { + public static Function1 OK = Function1.of(s -> new Message(s, Status.OK)); + public static Function1 BAD_REQUEST = Function1.of(s -> new Message(s, Status.BAD_REQUEST)); + public static Function1 CREATED = Function1.of(s -> new Message(s, Status.CREATED)); + public static Function1 NOT_FOUND = Function1.of(s -> new Message(s, Status.NOT_FOUND)); +} diff --git a/src/main/java/nouritsu/types/ShoppingItem.java b/src/main/java/nouritsu/types/ShoppingItem.java new file mode 100644 index 0000000..9809916 --- /dev/null +++ b/src/main/java/nouritsu/types/ShoppingItem.java @@ -0,0 +1,4 @@ +package nouritsu.types; + +public record ShoppingItem(String name, Category category) { +} diff --git a/src/main/java/nouritsu/types/Store.java b/src/main/java/nouritsu/types/Store.java new file mode 100644 index 0000000..04b08ab --- /dev/null +++ b/src/main/java/nouritsu/types/Store.java @@ -0,0 +1,9 @@ +package nouritsu.types; + +import java.util.List; + +/** + * A store with an ordered list of sections from entrance to exit. + */ +public record Store(String name, List sections) { +}