mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
[server] Add server module
This commit is contained in:
parent
523c76dd38
commit
e59e09aeb8
@ -6,6 +6,10 @@ ext {
|
|||||||
socket: '1.0.2', // io.socket:socket.io-client
|
socket: '1.0.2', // io.socket:socket.io-client
|
||||||
jline: '2.14.5', // jline:jline
|
jline: '2.14.5', // jline:jline
|
||||||
|
|
||||||
|
javalin: '5.6.3', // io.javalin:javalin
|
||||||
|
slf4j: '2.0.9', // org.slf4j:slf4j-simple
|
||||||
|
jackson: '2.15.3', // com.fasterxml.jackson.core:jackson-databind
|
||||||
|
|
||||||
junit: '5.9.2', // org.junit:junit-bom
|
junit: '5.9.2', // org.junit:junit-bom
|
||||||
jmh: '1.37', // org.openjdk.jmh:jmh-core
|
jmh: '1.37', // org.openjdk.jmh:jmh-core
|
||||||
assertj: '3.24.2' // org.assertj:assertj-core
|
assertj: '3.24.2' // org.assertj:assertj-core
|
||||||
|
21
modules/server/build.gradle
Normal file
21
modules/server/build.gradle
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java-library'
|
||||||
|
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.annimon.module'
|
||||||
|
version = '2.0-SNAPSHOT'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnlyApi project(":ownlang-core")
|
||||||
|
implementation "io.javalin:javalin:${versions.javalin}"
|
||||||
|
implementation "org.slf4j:slf4j-simple:${versions.slf4j}"
|
||||||
|
implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
|
||||||
|
|
||||||
|
testImplementation platform("org.junit:junit-bom:")
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.annimon.ownlang.modules.server;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
import io.javalin.http.Context;
|
||||||
|
import io.javalin.http.HttpStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ContextValue extends MapValue {
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
|
||||||
|
public ContextValue(@NotNull Context ctx) {
|
||||||
|
super(10);
|
||||||
|
this.ctx = ctx;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
set("body", Converters.voidToString(ctx::body));
|
||||||
|
set("characterEncoding", Converters.voidToString(ctx::characterEncoding));
|
||||||
|
set("contentType", Converters.voidToString(ctx::contentType));
|
||||||
|
set("contextPath", Converters.voidToString(ctx::contextPath));
|
||||||
|
set("fullUrl", Converters.voidToString(ctx::fullUrl));
|
||||||
|
set("host", Converters.voidToString(ctx::host));
|
||||||
|
set("ip", Converters.voidToString(ctx::ip));
|
||||||
|
set("matchedPath", Converters.voidToString(ctx::matchedPath));
|
||||||
|
set("path", Converters.voidToString(ctx::path));
|
||||||
|
set("protocol", Converters.voidToString(ctx::protocol));
|
||||||
|
set("queryString", Converters.voidToString(ctx::queryString));
|
||||||
|
set("url", Converters.voidToString(ctx::url));
|
||||||
|
set("userAgent", Converters.voidToString(ctx::userAgent));
|
||||||
|
|
||||||
|
set("contentLength", Converters.voidToInt(ctx::contentLength));
|
||||||
|
set("port", Converters.voidToInt(ctx::port));
|
||||||
|
set("statusCode", Converters.voidToInt(ctx::statusCode));
|
||||||
|
|
||||||
|
set("json", objectToContext(ctx::json));
|
||||||
|
set("jsonStream", objectToContext(ctx::jsonStream));
|
||||||
|
|
||||||
|
set("render", this::render);
|
||||||
|
set("result", this::result);
|
||||||
|
set("redirect", this::redirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value render(Value[] args) {
|
||||||
|
Arguments.checkOrOr(1, 2, args.length);
|
||||||
|
String filePath = args[0].asString();
|
||||||
|
if (args.length == 1) {
|
||||||
|
ctx.render(filePath);
|
||||||
|
} else {
|
||||||
|
MapValue map = (MapValue) args[1];
|
||||||
|
Map<String, Object> data = new HashMap<>(map.size());
|
||||||
|
map.getMap().forEach((k, v) -> data.put(k.asString(), v.asJavaObject()));
|
||||||
|
ctx.render(filePath, data);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value redirect(Value[] args) {
|
||||||
|
Arguments.checkOrOr(1, 2, args.length);
|
||||||
|
HttpStatus status = args.length == 1 ? HttpStatus.FOUND : HttpStatus.forStatus(args[1].asInt());
|
||||||
|
ctx.redirect(args[0].asString(), status);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value result(Value[] args) {
|
||||||
|
Arguments.checkOrOr(0, 1, args.length);
|
||||||
|
if (args.length == 0) {
|
||||||
|
return new StringValue(ctx.result());
|
||||||
|
} else {
|
||||||
|
final var arg = args[0];
|
||||||
|
if (arg.type() == Types.ARRAY) {
|
||||||
|
ctx.result(ValueUtils.toByteArray((ArrayValue) arg));
|
||||||
|
} else {
|
||||||
|
ctx.result(arg.asString());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value objectToContext(Consumer<Object> consumer) {
|
||||||
|
return new FunctionValue(args -> {
|
||||||
|
Arguments.check(1, args.length);
|
||||||
|
consumer.accept(args[0].asJavaObject());
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.annimon.ownlang.modules.server;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
import io.javalin.Javalin;
|
||||||
|
import io.javalin.http.Handler;
|
||||||
|
import io.javalin.security.RouteRole;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ServerValue extends MapValue {
|
||||||
|
|
||||||
|
private final Javalin server;
|
||||||
|
|
||||||
|
public ServerValue(Javalin server) {
|
||||||
|
super(10);
|
||||||
|
this.server = server;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
set("get", httpMethod(server::get));
|
||||||
|
set("post", httpMethod(server::post));
|
||||||
|
set("put", httpMethod(server::put));
|
||||||
|
set("patch", httpMethod(server::patch));
|
||||||
|
set("head", httpMethod(server::head));
|
||||||
|
set("delete", httpMethod(server::delete));
|
||||||
|
set("options", httpMethod(server::options));
|
||||||
|
set("error", this::error);
|
||||||
|
set("start", this::start);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value error(Value[] args) {
|
||||||
|
Arguments.checkOrOr(2, 3, args.length);
|
||||||
|
final int handlerIndex;
|
||||||
|
final String contentType;
|
||||||
|
if (args.length == 2) {
|
||||||
|
contentType = "*";
|
||||||
|
handlerIndex = 1;
|
||||||
|
} else {
|
||||||
|
contentType = args[1].asString();
|
||||||
|
handlerIndex = 2;
|
||||||
|
}
|
||||||
|
int status = args[0].asInt();
|
||||||
|
final Handler handler = toHandler(ValueUtils.consumeFunction(args[handlerIndex], handlerIndex));
|
||||||
|
server.error(status, contentType, handler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value start(Value[] args) {
|
||||||
|
Arguments.checkRange(0, 2, args.length);
|
||||||
|
switch (args.length) {
|
||||||
|
case 0 -> server.start();
|
||||||
|
case 1 -> server.start(args[0].asInt());
|
||||||
|
case 2 -> server.start(args[0].asString(), args[1].asInt());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionValue httpMethod(HttpMethodHandler httpHandler) {
|
||||||
|
return new FunctionValue(args -> {
|
||||||
|
Arguments.checkAtLeast(2, args.length);
|
||||||
|
final String path = args[0].asString();
|
||||||
|
final Handler handler = toHandler(ValueUtils.consumeFunction(args[1], 1));
|
||||||
|
final Role[] roles;
|
||||||
|
if (args.length == 2) {
|
||||||
|
roles = new Role[0];
|
||||||
|
} else {
|
||||||
|
roles = Arrays.stream(args)
|
||||||
|
.skip(2)
|
||||||
|
.map(Role::new)
|
||||||
|
.toArray(Role[]::new);
|
||||||
|
}
|
||||||
|
httpHandler.handle(path, handler, roles);
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Handler toHandler(Function function) {
|
||||||
|
return ctx -> function.execute(new ContextValue(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface HttpMethodHandler {
|
||||||
|
void handle(String path, Handler handler, RouteRole[] roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Role(Value value) implements RouteRole { }
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.annimon.ownlang.modules.server;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
import com.annimon.ownlang.modules.Module;
|
||||||
|
import io.javalin.Javalin;
|
||||||
|
import java.util.Map;
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
|
||||||
|
public final class server implements Module {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Value> constants() {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Function> functions() {
|
||||||
|
return Map.ofEntries(
|
||||||
|
entry("newServer", this::newServer),
|
||||||
|
entry("serve", this::serve)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value newServer(Value[] args) {
|
||||||
|
Arguments.checkOrOr(0, 1, args.length);
|
||||||
|
if (args.length == 0) {
|
||||||
|
return new ServerValue(Javalin.create());
|
||||||
|
} else {
|
||||||
|
final Function configConsumer = ValueUtils.consumeFunction(args[0], 0);
|
||||||
|
return new ServerValue(Javalin.create(config -> configConsumer.execute()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value serve(Value[] args) {
|
||||||
|
// get path, port
|
||||||
|
// javalin start()
|
||||||
|
return NumberValue.ZERO;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,9 @@ include 'ownlang-parser'
|
|||||||
include 'ownlang-desktop'
|
include 'ownlang-desktop'
|
||||||
include 'ownlang-utils'
|
include 'ownlang-utils'
|
||||||
|
|
||||||
include 'modules:main'
|
final def modules = ['main', 'canvasfx', 'server']
|
||||||
findProject(':modules:main')?.name = 'main'
|
|
||||||
include 'modules:canvasfx'
|
for (final def module in modules) {
|
||||||
findProject(':modules:canvasfx')?.name = 'canvasfx'
|
include "modules:$module"
|
||||||
|
findProject(":modules:$module")?.name = module
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user