skir-cc-gen 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -43
- package/client/CMakeLists.txt +90 -0
- package/client/skir.cc +66 -9
- package/client/skir.h +184 -155
- package/client/skir.testing.h +2 -3
- package/dist/enum_variant.js +2 -2
- package/dist/enum_variant.js.map +1 -1
- package/dist/type_speller.js +1 -1
- package/package.json +3 -3
- package/src/enum_variant.ts +2 -2
- package/src/type_speller.ts +1 -1
package/README.md
CHANGED
|
@@ -7,20 +7,65 @@ Official plugin for generating C++ code from [.skir](https://github.com/gepheum/
|
|
|
7
7
|
|
|
8
8
|
Targets C++17 and higher.
|
|
9
9
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
From your project's root directory, run `npm i --save-dev skir-cc-gen`.
|
|
10
|
+
## Set up
|
|
13
11
|
|
|
14
12
|
In your `skir.yml` file, add the following snippet under `generators`:
|
|
15
13
|
```yaml
|
|
16
14
|
- mod: skir-cc-gen
|
|
15
|
+
outDir: ./src/skirout
|
|
17
16
|
config:
|
|
18
|
-
writeGoogleTestHeaders: true
|
|
17
|
+
writeGoogleTestHeaders: true # If you use GoogleTest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Runtime dependencies
|
|
21
|
+
|
|
22
|
+
The generated C++ code depends on the [skir client library](https://github.com/gepheum/skir-cc-gen/tree/main/client), [absl](https://abseil.io/) and optionally [GoogleTest](https://github.com/google/googletest).
|
|
23
|
+
|
|
24
|
+
### If you use CMake
|
|
25
|
+
|
|
26
|
+
Add this to your `CMakeLists.txt`:
|
|
27
|
+
|
|
28
|
+
```cmake
|
|
29
|
+
include(FetchContent)
|
|
30
|
+
|
|
31
|
+
# Should be ON if writeGoogleTestHeaders is true
|
|
32
|
+
option(BUILD_TESTING "Build tests" OFF)
|
|
33
|
+
|
|
34
|
+
# Abseil (required, version 20250814.0+)
|
|
35
|
+
FetchContent_Declare(
|
|
36
|
+
absl
|
|
37
|
+
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
|
|
38
|
+
GIT_TAG 20250814.1 # Use 20250814.0 or later
|
|
39
|
+
)
|
|
40
|
+
set(ABSL_PROPAGATE_CXX_STD ON)
|
|
41
|
+
FetchContent_MakeAvailable(absl)
|
|
42
|
+
|
|
43
|
+
if(BUILD_TESTING)
|
|
44
|
+
# GoogleTest (optional - only if you use writeGoogleTestHeaders)
|
|
45
|
+
FetchContent_Declare(
|
|
46
|
+
googletest
|
|
47
|
+
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
48
|
+
GIT_TAG v1.15.2 # Pick the latest tag
|
|
49
|
+
)
|
|
50
|
+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
51
|
+
FetchContent_MakeAvailable(googletest)
|
|
52
|
+
endif()
|
|
53
|
+
|
|
54
|
+
# skir-client
|
|
55
|
+
FetchContent_Declare(
|
|
56
|
+
skir-client
|
|
57
|
+
GIT_REPOSITORY https://github.com/gepheum/skir-cc-gen.git
|
|
58
|
+
GIT_TAG main # Or pick a specific commit/tag
|
|
59
|
+
SOURCE_SUBDIR client
|
|
60
|
+
)
|
|
61
|
+
FetchContent_MakeAvailable(skir-client)
|
|
19
62
|
```
|
|
20
63
|
|
|
21
|
-
|
|
64
|
+
See this [example](https://github.com/gepheum/skir-cc-example/blob/main/CMakeLists.txt).
|
|
65
|
+
|
|
66
|
+
### If you use Bazel
|
|
22
67
|
|
|
23
|
-
|
|
68
|
+
Refer to this example [BUILD.bazel](https://github.com/gepheum/skir-cc-example/blob/main/BUILD.bazel) file.
|
|
24
69
|
|
|
25
70
|
## C++ generated code guide
|
|
26
71
|
|
|
@@ -36,6 +81,7 @@ replaced with underscores.
|
|
|
36
81
|
```c++
|
|
37
82
|
#include "skirout/user.h"
|
|
38
83
|
|
|
84
|
+
using ::skirout_user::SubscriptionStatus;
|
|
39
85
|
using ::skirout_user::User;
|
|
40
86
|
using ::skirout_user::UserRegistry;
|
|
41
87
|
```
|
|
@@ -64,7 +110,7 @@ User jane = {
|
|
|
64
110
|
.user_id = 43,
|
|
65
111
|
};
|
|
66
112
|
|
|
67
|
-
// ${
|
|
113
|
+
// ${Struct}::whole forces you to initialize all the fields of the struct.
|
|
68
114
|
// You will get a compile-time error if you miss one.
|
|
69
115
|
User lyla = User::whole{
|
|
70
116
|
.name = "Lyla Doe",
|
|
@@ -86,20 +132,22 @@ User lyla = User::whole{
|
|
|
86
132
|
|
|
87
133
|
```c++
|
|
88
134
|
|
|
89
|
-
// Use skirout::${kFieldName} for constant variants.
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
// Use skirout::${kFieldName} or ${Enum}::${kFieldName} for constant variants.
|
|
136
|
+
SubscriptionStatus john_status = skirout::kFree;
|
|
137
|
+
SubscriptionStatus jane_status = skirout::kPremium;
|
|
138
|
+
SubscriptionStatus lara_status = SubscriptionStatus::kFree;
|
|
92
139
|
|
|
93
140
|
// Compilation error: MONDAY is not a field of the SubscriptionStatus enum.
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
// Use
|
|
97
|
-
|
|
98
|
-
skirout::
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
141
|
+
// SubscriptionStatus sara_status = skirout::kMonday;
|
|
142
|
+
|
|
143
|
+
// Use wrap_${field_name} for wrapper variants.
|
|
144
|
+
SubscriptionStatus jade_status =
|
|
145
|
+
skirout::wrap_trial(SubscriptionStatus::Trial({
|
|
146
|
+
.start_time = absl::FromUnixMillis(1743682787000),
|
|
147
|
+
}));
|
|
148
|
+
SubscriptionStatus roni_status = SubscriptionStatus::wrap_trial({
|
|
149
|
+
.start_time = absl::FromUnixMillis(1743682787000),
|
|
150
|
+
});
|
|
103
151
|
```
|
|
104
152
|
|
|
105
153
|
### Conditions on enums
|
|
@@ -109,28 +157,28 @@ if (john_status == skirout::kFree) {
|
|
|
109
157
|
std::cout << "John, would you like to upgrade to premium?\n";
|
|
110
158
|
}
|
|
111
159
|
|
|
112
|
-
// Call is_${field_name}() to check if the enum holds a
|
|
113
|
-
if (jade_status.
|
|
114
|
-
// as_${field_name}() returns the value
|
|
115
|
-
const
|
|
116
|
-
std::cout << "Jade's trial started on " <<
|
|
160
|
+
// Call is_${field_name}() to check if the enum holds a wrapper variant.
|
|
161
|
+
if (jade_status.is_trial()) {
|
|
162
|
+
// as_${field_name}() returns the wrapped value
|
|
163
|
+
const SubscriptionStatus::Trial& trial = jade_status.as_trial();
|
|
164
|
+
std::cout << "Jade's trial started on " << trial.start_time << "\n";
|
|
117
165
|
}
|
|
118
166
|
|
|
119
167
|
// One way to do an exhaustive switch on an enum.
|
|
120
168
|
switch (lara_status.kind()) {
|
|
121
|
-
case
|
|
169
|
+
case SubscriptionStatus::kind_type::kUnknown:
|
|
122
170
|
// UNKNOWN is the default value for an uninitialized SubscriptionStatus.
|
|
123
171
|
// ...
|
|
124
172
|
break;
|
|
125
|
-
case
|
|
173
|
+
case SubscriptionStatus::kind_type::kFreeConst:
|
|
126
174
|
// ...
|
|
127
175
|
break;
|
|
128
|
-
case
|
|
176
|
+
case SubscriptionStatus::kind_type::kPremiumConst:
|
|
129
177
|
// ...
|
|
130
178
|
break;
|
|
131
|
-
case
|
|
132
|
-
const
|
|
133
|
-
std::cout << "Lara's trial started on " <<
|
|
179
|
+
case SubscriptionStatus::kind_type::kTrialWrapper: {
|
|
180
|
+
const SubscriptionStatus::Trial& trial = lara_status.as_trial();
|
|
181
|
+
std::cout << "Lara's trial started on " << trial.start_time << "\n";
|
|
134
182
|
}
|
|
135
183
|
}
|
|
136
184
|
|
|
@@ -145,10 +193,9 @@ struct Visitor {
|
|
|
145
193
|
void operator()(skirout::k_premium) const {
|
|
146
194
|
std::cout << "Lara's subscription status is PREMIUM\n";
|
|
147
195
|
}
|
|
148
|
-
void operator()(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
std::cout << "Lara's trial started on " << trial_start_time << "\n";
|
|
196
|
+
void operator()(SubscriptionStatus::wrap_trial_type& w) const {
|
|
197
|
+
const SubscriptionStatus::Trial& trial = w.value;
|
|
198
|
+
std::cout << "Lara's trial started on " << trial.start_time << "\n";
|
|
152
199
|
}
|
|
153
200
|
};
|
|
154
201
|
lara_status.visit(Visitor());
|
|
@@ -212,6 +259,13 @@ assert(maybe_jane != nullptr && *maybe_jane == jane);
|
|
|
212
259
|
|
|
213
260
|
assert(users.find_or_default(44).name == "Lyla Doe");
|
|
214
261
|
assert(users.find_or_default(45).name == "");
|
|
262
|
+
|
|
263
|
+
// If multiple items have the same key, find_or_null and find_or_default
|
|
264
|
+
// return the last one. Duplicates are allowed but generally discouraged.
|
|
265
|
+
User evil_lyla = lyla;
|
|
266
|
+
evil_lyla.name = "Evil Lyla";
|
|
267
|
+
users.push_back(evil_lyla);
|
|
268
|
+
assert(users.find_or_default(44).name == "Evil Lyla");
|
|
215
269
|
```
|
|
216
270
|
|
|
217
271
|
### Equality and hashing
|
|
@@ -265,7 +319,7 @@ assert(reserialized_type_descriptor.ok());
|
|
|
265
319
|
### Static reflection
|
|
266
320
|
|
|
267
321
|
Static reflection allows you to inspect and modify values of generated
|
|
268
|
-
skir types in a typesafe
|
|
322
|
+
skir types in a typesafe manner.
|
|
269
323
|
|
|
270
324
|
See [string_capitalizer.h](https://github.com/gepheum/skir-cc-example/blob/main/string_capitalizer.h).
|
|
271
325
|
|
|
@@ -287,10 +341,10 @@ std::cout << tarzan_copy << "\n";
|
|
|
287
341
|
// .picture: "🐒",
|
|
288
342
|
// },
|
|
289
343
|
// },
|
|
290
|
-
// .subscription_status:
|
|
344
|
+
// .subscription_status:
|
|
345
|
+
// ::skirout::wrap_trial_start_time(absl::FromUnixMillis(1743592409000 /*
|
|
346
|
+
// 2025-04-02T11:13:29+00:00 */)),
|
|
291
347
|
// }
|
|
292
|
-
|
|
293
|
-
// ...
|
|
294
348
|
```
|
|
295
349
|
|
|
296
350
|
### Writing unit tests with GoogleTest
|
|
@@ -323,13 +377,14 @@ EXPECT_THAT(john, (StructIs<User>{
|
|
|
323
377
|
#### Enum matchers
|
|
324
378
|
|
|
325
379
|
```c++
|
|
326
|
-
|
|
380
|
+
SubscriptionStatus john_status = skirout::kFree;
|
|
327
381
|
|
|
328
382
|
EXPECT_THAT(john_status, testing::Eq(skirout::kFree));
|
|
329
383
|
|
|
330
|
-
|
|
331
|
-
|
|
384
|
+
SubscriptionStatus jade_status = SubscriptionStatus::wrap_trial(
|
|
385
|
+
{.start_time = absl::FromUnixMillis(1743682787000)});
|
|
332
386
|
|
|
333
|
-
EXPECT_THAT(jade_status,
|
|
334
|
-
EXPECT_THAT(jade_status,
|
|
387
|
+
EXPECT_THAT(jade_status, IsTrial());
|
|
388
|
+
EXPECT_THAT(jade_status, IsTrial(StructIs<SubscriptionStatus::Trial>{
|
|
389
|
+
.start_time = testing::Gt(absl::UnixEpoch())}));
|
|
335
390
|
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.20)
|
|
2
|
+
project(skir-client CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
|
|
7
|
+
# ============================================================================
|
|
8
|
+
# Dependencies - Use existing targets if available, otherwise find them
|
|
9
|
+
# ============================================================================
|
|
10
|
+
|
|
11
|
+
# Abseil - check if already provided by parent project
|
|
12
|
+
if(NOT TARGET absl::base)
|
|
13
|
+
find_package(absl REQUIRED)
|
|
14
|
+
endif()
|
|
15
|
+
|
|
16
|
+
# GoogleTest - optional, only needed for testing library
|
|
17
|
+
if(NOT DEFINED SKIR_ENABLE_TESTING)
|
|
18
|
+
# Default: enable testing if GTest is available
|
|
19
|
+
if(TARGET GTest::gtest)
|
|
20
|
+
set(SKIR_ENABLE_TESTING ON)
|
|
21
|
+
else()
|
|
22
|
+
find_package(GTest QUIET)
|
|
23
|
+
if(GTest_FOUND)
|
|
24
|
+
set(SKIR_ENABLE_TESTING ON)
|
|
25
|
+
else()
|
|
26
|
+
set(SKIR_ENABLE_TESTING OFF)
|
|
27
|
+
endif()
|
|
28
|
+
endif()
|
|
29
|
+
endif()
|
|
30
|
+
|
|
31
|
+
message(STATUS "Skir testing library: ${SKIR_ENABLE_TESTING}")
|
|
32
|
+
|
|
33
|
+
# ============================================================================
|
|
34
|
+
# Main skir library
|
|
35
|
+
# ============================================================================
|
|
36
|
+
|
|
37
|
+
add_library(skir
|
|
38
|
+
skir.cc
|
|
39
|
+
skir.h
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
target_include_directories(skir PUBLIC
|
|
43
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
44
|
+
$<INSTALL_INTERFACE:include>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
target_link_libraries(skir PUBLIC
|
|
48
|
+
absl::base
|
|
49
|
+
absl::flat_hash_map
|
|
50
|
+
absl::hash
|
|
51
|
+
absl::check
|
|
52
|
+
absl::die_if_null
|
|
53
|
+
absl::status
|
|
54
|
+
absl::statusor
|
|
55
|
+
absl::strings
|
|
56
|
+
absl::time
|
|
57
|
+
absl::optional
|
|
58
|
+
absl::variant
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# ============================================================================
|
|
62
|
+
# Testing library (interface library with header-only implementation)
|
|
63
|
+
# Only built if GTest is available
|
|
64
|
+
# ============================================================================
|
|
65
|
+
|
|
66
|
+
if(SKIR_ENABLE_TESTING)
|
|
67
|
+
add_library(skir_testing INTERFACE)
|
|
68
|
+
|
|
69
|
+
target_sources(skir_testing INTERFACE
|
|
70
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/skir.testing.h>
|
|
71
|
+
$<INSTALL_INTERFACE:include/skir.testing.h>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
target_include_directories(skir_testing INTERFACE
|
|
75
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
76
|
+
$<INSTALL_INTERFACE:include>
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
target_link_libraries(skir_testing INTERFACE
|
|
80
|
+
skir
|
|
81
|
+
absl::base
|
|
82
|
+
absl::die_if_null
|
|
83
|
+
GTest::gtest
|
|
84
|
+
GTest::gmock
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
message(STATUS "Skir testing library target created")
|
|
88
|
+
else()
|
|
89
|
+
message(STATUS "Skir testing library disabled (GTest not available)")
|
|
90
|
+
endif()
|
package/client/skir.cc
CHANGED
|
@@ -996,6 +996,63 @@ void ParseFieldOrVariant(JsonTokenizer& tokenizer, FieldOrVariant& out) {
|
|
|
996
996
|
}
|
|
997
997
|
} // namespace
|
|
998
998
|
|
|
999
|
+
skir::service::RawResponse MakeOkJsonResponse(std::string data) {
|
|
1000
|
+
return {
|
|
1001
|
+
.data = std::move(data),
|
|
1002
|
+
.status_code = 200,
|
|
1003
|
+
.content_type = "application/json",
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
skir::service::RawResponse MakeOkHtmlResponse(std::string data) {
|
|
1008
|
+
return {
|
|
1009
|
+
.data = std::move(data),
|
|
1010
|
+
.status_code = 200,
|
|
1011
|
+
.content_type = "text/html; charset=utf-8",
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
std::string GetStudioHtml(absl::string_view studio_app_js_url) {
|
|
1016
|
+
const std::string escaped_url =
|
|
1017
|
+
absl::StrReplaceAll(studio_app_js_url, {{"&", "&"},
|
|
1018
|
+
{"<", "<"},
|
|
1019
|
+
{">", ">"},
|
|
1020
|
+
{"\"", """},
|
|
1021
|
+
{"'", "'"}});
|
|
1022
|
+
return absl::StrCat(R"html(<!DOCTYPE html>
|
|
1023
|
+
|
|
1024
|
+
<html>
|
|
1025
|
+
<head>
|
|
1026
|
+
<meta charset="utf-8" />
|
|
1027
|
+
<title>Skir Studio</title>
|
|
1028
|
+
<script src=")html",
|
|
1029
|
+
escaped_url,
|
|
1030
|
+
R"html("></script>
|
|
1031
|
+
</head>
|
|
1032
|
+
<body style="margin: 0; padding: 0;">
|
|
1033
|
+
<skir-studio-app></skir-studio-app>
|
|
1034
|
+
</body>
|
|
1035
|
+
</html>
|
|
1036
|
+
)html");
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
skir::service::RawResponse MakeBadRequestResponse(std::string data) {
|
|
1040
|
+
return {
|
|
1041
|
+
.data = std::move(data),
|
|
1042
|
+
.status_code = 400,
|
|
1043
|
+
.content_type = "text/plain; charset=utf-8",
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
skir::service::RawResponse MakeServerErrorResponse(std::string data,
|
|
1048
|
+
int status_code) {
|
|
1049
|
+
return {
|
|
1050
|
+
.data = std::move(data),
|
|
1051
|
+
.status_code = status_code,
|
|
1052
|
+
.content_type = "text/plain; charset=utf-8",
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
999
1056
|
JsonTokenType JsonTokenizer::Next() {
|
|
1000
1057
|
if (!state_.status.ok()) return JsonTokenType::kError;
|
|
1001
1058
|
return state_.token_type = NextImpl(state_);
|
|
@@ -1368,7 +1425,7 @@ void Int64Adapter::Parse(ByteSource& source, int64_t& out) {
|
|
|
1368
1425
|
ParseNumber(source, out);
|
|
1369
1426
|
}
|
|
1370
1427
|
|
|
1371
|
-
void
|
|
1428
|
+
void Hash64Adapter::Append(uint64_t input, ByteSink& out) {
|
|
1372
1429
|
if (input < 232) {
|
|
1373
1430
|
out.Push(input);
|
|
1374
1431
|
} else if (input < 4294967296) {
|
|
@@ -1382,11 +1439,11 @@ void Uint64Adapter::Append(uint64_t input, ByteSink& out) {
|
|
|
1382
1439
|
}
|
|
1383
1440
|
}
|
|
1384
1441
|
|
|
1385
|
-
void
|
|
1442
|
+
void Hash64Adapter::Parse(JsonTokenizer& tokenizer, uint64_t& out) {
|
|
1386
1443
|
ParseJsonNumber(tokenizer, out);
|
|
1387
1444
|
}
|
|
1388
1445
|
|
|
1389
|
-
void
|
|
1446
|
+
void Hash64Adapter::Parse(ByteSource& source, uint64_t& out) {
|
|
1390
1447
|
ParseNumber(source, out);
|
|
1391
1448
|
}
|
|
1392
1449
|
|
|
@@ -1730,8 +1787,8 @@ void ReflectionPrimitiveTypeAdapter::Append(
|
|
|
1730
1787
|
out.out += "\"int64\"";
|
|
1731
1788
|
break;
|
|
1732
1789
|
}
|
|
1733
|
-
case skir::reflection::PrimitiveType::
|
|
1734
|
-
out.out += "\"
|
|
1790
|
+
case skir::reflection::PrimitiveType::kHash64: {
|
|
1791
|
+
out.out += "\"hash64\"";
|
|
1735
1792
|
break;
|
|
1736
1793
|
}
|
|
1737
1794
|
case skir::reflection::PrimitiveType::kFloat32: {
|
|
@@ -1764,7 +1821,7 @@ void ReflectionPrimitiveTypeAdapter::Parse(
|
|
|
1764
1821
|
{"bool", skir::reflection::PrimitiveType::kBool},
|
|
1765
1822
|
{"int32", skir::reflection::PrimitiveType::kInt32},
|
|
1766
1823
|
{"int64", skir::reflection::PrimitiveType::kInt64},
|
|
1767
|
-
{"
|
|
1824
|
+
{"hash64", skir::reflection::PrimitiveType::kHash64},
|
|
1768
1825
|
{"float32", skir::reflection::PrimitiveType::kFloat32},
|
|
1769
1826
|
{"float64", skir::reflection::PrimitiveType::kFloat64},
|
|
1770
1827
|
{"timestamp", skir::reflection::PrimitiveType::kTimestamp},
|
|
@@ -2098,7 +2155,7 @@ void UnrecognizedValues::ParseFrom(JsonTokenizer& tokenizer) {
|
|
|
2098
2155
|
break;
|
|
2099
2156
|
}
|
|
2100
2157
|
case JsonTokenType::kUnsignedInteger: {
|
|
2101
|
-
|
|
2158
|
+
Hash64Adapter::Append(tokenizer.state().uint_value, bytes_);
|
|
2102
2159
|
tokenizer.Next();
|
|
2103
2160
|
break;
|
|
2104
2161
|
}
|
|
@@ -2252,8 +2309,8 @@ void UnrecognizedValues::AppendTo(DenseJson& out) const {
|
|
|
2252
2309
|
const uint8_t byte = *source.pos;
|
|
2253
2310
|
if (byte <= 234) {
|
|
2254
2311
|
uint64_t number = 0;
|
|
2255
|
-
|
|
2256
|
-
|
|
2312
|
+
Hash64Adapter::Parse(source, number);
|
|
2313
|
+
Hash64Adapter::Append(number, out);
|
|
2257
2314
|
} else {
|
|
2258
2315
|
switch (static_cast<uint8_t>(byte - 235)) {
|
|
2259
2316
|
case 0:
|
package/client/skir.h
CHANGED
|
@@ -432,7 +432,7 @@ class keyed_items {
|
|
|
432
432
|
kUint8,
|
|
433
433
|
kUint16,
|
|
434
434
|
kUint32,
|
|
435
|
-
|
|
435
|
+
kHash64,
|
|
436
436
|
};
|
|
437
437
|
SlotType slot_type_ = SlotType::kUint8;
|
|
438
438
|
bool being_mutated_ = false;
|
|
@@ -477,7 +477,7 @@ class keyed_items {
|
|
|
477
477
|
case SlotType::kUint32:
|
|
478
478
|
PutSlot<uint32_t>(key_hash, next_index);
|
|
479
479
|
break;
|
|
480
|
-
case SlotType::
|
|
480
|
+
case SlotType::kHash64:
|
|
481
481
|
PutSlot<uint64_t>(key_hash, next_index);
|
|
482
482
|
break;
|
|
483
483
|
}
|
|
@@ -573,7 +573,7 @@ class keyed_items {
|
|
|
573
573
|
} else if (capacity <= std::numeric_limits<uint32_t>::max()) {
|
|
574
574
|
return SlotType::kUint32;
|
|
575
575
|
} else {
|
|
576
|
-
return SlotType::
|
|
576
|
+
return SlotType::kHash64;
|
|
577
577
|
}
|
|
578
578
|
}
|
|
579
579
|
|
|
@@ -765,7 +765,7 @@ enum class PrimitiveType {
|
|
|
765
765
|
kBool,
|
|
766
766
|
kInt32,
|
|
767
767
|
kInt64,
|
|
768
|
-
|
|
768
|
+
kHash64,
|
|
769
769
|
kFloat32,
|
|
770
770
|
kFloat64,
|
|
771
771
|
kTimestamp,
|
|
@@ -882,64 +882,120 @@ struct enum_wrapper_variant {
|
|
|
882
882
|
|
|
883
883
|
namespace service {
|
|
884
884
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
885
|
+
// HTTP status codes for errors.
|
|
886
|
+
enum class HttpErrorCode {
|
|
887
|
+
k400_BadRequest = 400,
|
|
888
|
+
k401_Unauthorized = 401,
|
|
889
|
+
k402_PaymentRequired = 402,
|
|
890
|
+
k403_Forbidden = 403,
|
|
891
|
+
k404_NotFound = 404,
|
|
892
|
+
k405_MethodNotAllowed = 405,
|
|
893
|
+
k406_NotAcceptable = 406,
|
|
894
|
+
k407_ProxyAuthenticationRequired = 407,
|
|
895
|
+
k408_RequestTimeout = 408,
|
|
896
|
+
k409_Conflict = 409,
|
|
897
|
+
k410_Gone = 410,
|
|
898
|
+
k411_LengthRequired = 411,
|
|
899
|
+
k412_PreconditionFailed = 412,
|
|
900
|
+
k413_ContentTooLarge = 413,
|
|
901
|
+
k414_UriTooLong = 414,
|
|
902
|
+
k415_UnsupportedMediaType = 415,
|
|
903
|
+
k416_RangeNotSatisfiable = 416,
|
|
904
|
+
k417_ExpectationFailed = 417,
|
|
905
|
+
k418_ImATeapot = 418,
|
|
906
|
+
k421_MisdirectedRequest = 421,
|
|
907
|
+
k422_UnprocessableContent = 422,
|
|
908
|
+
k423_Locked = 423,
|
|
909
|
+
k424_FailedDependency = 424,
|
|
910
|
+
k425_TooEarly = 425,
|
|
911
|
+
k426_UpgradeRequired = 426,
|
|
912
|
+
k428_PreconditionRequired = 428,
|
|
913
|
+
k429_TooManyRequests = 429,
|
|
914
|
+
k431_RequestHeaderFieldsTooLarge = 431,
|
|
915
|
+
k451_UnavailableForLegalReasons = 451,
|
|
916
|
+
k500_InternalServerError = 500,
|
|
917
|
+
k501_NotImplemented = 501,
|
|
918
|
+
k502_BadGateway = 502,
|
|
919
|
+
k503_ServiceUnavailable = 503,
|
|
920
|
+
k504_GatewayTimeout = 504,
|
|
921
|
+
k505_HttpVersionNotSupported = 505,
|
|
922
|
+
k506_VariantAlsoNegotiates = 506,
|
|
923
|
+
k507_InsufficientStorage = 507,
|
|
924
|
+
k508_LoopDetected = 508,
|
|
925
|
+
k510_NotExtended = 510,
|
|
926
|
+
k511_NetworkAuthenticationRequired = 511,
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// Represents an error with a specific HTTP status code.
|
|
930
|
+
// Use this as a return type from service methods when you want to control the
|
|
931
|
+
// HTTP error code sent to clients. For generic errors where the default 500
|
|
932
|
+
// status code is acceptable, use absl::Status instead.
|
|
933
|
+
struct Error {
|
|
934
|
+
HttpErrorCode code = HttpErrorCode::k500_InternalServerError;
|
|
935
|
+
std::string message;
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
// Represents either a successful Response or an Error with a specific HTTP
|
|
939
|
+
// status code. Use this as the return type for service methods when you need
|
|
940
|
+
// fine-grained control over HTTP error codes. For simple error handling where
|
|
941
|
+
// generic HTTP status codes are sufficient, use absl::StatusOr<Response>
|
|
942
|
+
// instead.
|
|
943
|
+
template <typename Response>
|
|
944
|
+
class ErrorOr {
|
|
945
|
+
public:
|
|
946
|
+
ErrorOr() = default;
|
|
947
|
+
ErrorOr(const ErrorOr&) = default;
|
|
948
|
+
ErrorOr(ErrorOr&&) = default;
|
|
903
949
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
return 200;
|
|
909
|
-
case ResponseType::kBadRequest:
|
|
910
|
-
return 400;
|
|
911
|
-
case ResponseType::kServerError:
|
|
912
|
-
return 500;
|
|
913
|
-
}
|
|
950
|
+
ErrorOr(Response response) : variant_(std::move(response)) {}
|
|
951
|
+
ErrorOr(Error error) : variant_(std::move(error)) {}
|
|
952
|
+
ErrorOr(absl::StatusOr<Response> response_or_error) {
|
|
953
|
+
(*this) = std::move(response_or_error);
|
|
914
954
|
}
|
|
915
955
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
956
|
+
ErrorOr& operator=(const ErrorOr&) = default;
|
|
957
|
+
ErrorOr& operator=(ErrorOr&&) = default;
|
|
958
|
+
|
|
959
|
+
ErrorOr& operator=(Response response) {
|
|
960
|
+
variant_ = std::move(response);
|
|
961
|
+
return *this;
|
|
962
|
+
}
|
|
963
|
+
ErrorOr& operator=(Error error) {
|
|
964
|
+
variant_ = std::move(error);
|
|
965
|
+
return *this;
|
|
966
|
+
}
|
|
967
|
+
ErrorOr& operator=(absl::StatusOr<Response> response_or_error) {
|
|
968
|
+
if (response_or_error.ok()) {
|
|
969
|
+
variant_ = *std::move(response_or_error);
|
|
970
|
+
} else {
|
|
971
|
+
variant_ = Error{
|
|
972
|
+
HttpErrorCode::k500_InternalServerError,
|
|
973
|
+
absl::StrCat("server error: ", response_or_error.status().message())};
|
|
931
974
|
}
|
|
975
|
+
return *this;
|
|
932
976
|
}
|
|
933
977
|
|
|
978
|
+
bool ok() const { return std::holds_alternative<Response>(variant_); }
|
|
979
|
+
|
|
980
|
+
const std::variant<Response, Error>& variant() const { return variant_; }
|
|
981
|
+
std::variant<Response, Error>& variant() { return variant_; }
|
|
982
|
+
|
|
983
|
+
private:
|
|
984
|
+
using variant_type = std::variant<Response, Error>;
|
|
985
|
+
variant_type variant_;
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
// Raw response returned by the server.
|
|
989
|
+
struct RawResponse {
|
|
990
|
+
std::string data;
|
|
991
|
+
int status_code{};
|
|
992
|
+
std::string content_type;
|
|
993
|
+
|
|
934
994
|
absl::StatusOr<std::string> AsStatus() && {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
case ResponseType::kOkHtml:
|
|
938
|
-
return std::move(data);
|
|
939
|
-
case ResponseType::kBadRequest:
|
|
940
|
-
case ResponseType::kServerError:
|
|
941
|
-
return absl::UnknownError(std::move(data));
|
|
995
|
+
if (status_code >= 200 && status_code < 300) {
|
|
996
|
+
return std::move(data);
|
|
942
997
|
}
|
|
998
|
+
return absl::UnknownError(std::move(data));
|
|
943
999
|
}
|
|
944
1000
|
};
|
|
945
1001
|
|
|
@@ -975,14 +1031,22 @@ class HttpHeaders {
|
|
|
975
1031
|
absl::flat_hash_map<std::string, std::vector<std::string>> map_;
|
|
976
1032
|
};
|
|
977
1033
|
|
|
1034
|
+
// Options for handling service requests.
|
|
1035
|
+
struct ServiceOptions {
|
|
1036
|
+
UnrecognizedValuesPolicy unrecognized_values =
|
|
1037
|
+
UnrecognizedValuesPolicy::kDrop;
|
|
1038
|
+
std::string studio_app_js_url =
|
|
1039
|
+
"https://cdn.jsdelivr.net/npm/skir-studio/dist/skir-studio-standalone.js";
|
|
1040
|
+
};
|
|
1041
|
+
|
|
978
1042
|
// Sends RPCs to a skir service.
|
|
979
1043
|
class Client {
|
|
980
1044
|
public:
|
|
981
1045
|
virtual ~Client() = default;
|
|
982
1046
|
|
|
983
1047
|
virtual absl::StatusOr<std::string> operator()(
|
|
984
|
-
absl::string_view request_data,
|
|
985
|
-
HttpHeaders&
|
|
1048
|
+
absl::string_view request_data,
|
|
1049
|
+
const HttpHeaders& request_headers) const = 0;
|
|
986
1050
|
};
|
|
987
1051
|
|
|
988
1052
|
} // namespace service
|
|
@@ -990,6 +1054,12 @@ class Client {
|
|
|
990
1054
|
|
|
991
1055
|
namespace skir_internal {
|
|
992
1056
|
|
|
1057
|
+
skir::service::RawResponse MakeOkJsonResponse(std::string data);
|
|
1058
|
+
skir::service::RawResponse MakeOkHtmlResponse(std::string data);
|
|
1059
|
+
skir::service::RawResponse MakeBadRequestResponse(std::string data);
|
|
1060
|
+
skir::service::RawResponse MakeServerErrorResponse(std::string data,
|
|
1061
|
+
int status_code);
|
|
1062
|
+
|
|
993
1063
|
template <typename T>
|
|
994
1064
|
T& get(T& input) {
|
|
995
1065
|
return input;
|
|
@@ -1406,7 +1476,7 @@ struct Int64Adapter {
|
|
|
1406
1476
|
static constexpr bool IsEnum() { return false; }
|
|
1407
1477
|
};
|
|
1408
1478
|
|
|
1409
|
-
struct
|
|
1479
|
+
struct Hash64Adapter {
|
|
1410
1480
|
static bool IsDefault(uint64_t input) { return !input; }
|
|
1411
1481
|
|
|
1412
1482
|
template <typename Out>
|
|
@@ -1429,7 +1499,7 @@ struct Uint64Adapter {
|
|
|
1429
1499
|
static void Parse(ByteSource& source, uint64_t& out);
|
|
1430
1500
|
|
|
1431
1501
|
static skir::reflection::Type GetType(skir_type<uint64_t>) {
|
|
1432
|
-
return skir::reflection::PrimitiveType::
|
|
1502
|
+
return skir::reflection::PrimitiveType::kHash64;
|
|
1433
1503
|
}
|
|
1434
1504
|
|
|
1435
1505
|
static void RegisterRecords(skir_type<uint64_t>,
|
|
@@ -1603,7 +1673,7 @@ void GetAdapter(skir_type<T>) {
|
|
|
1603
1673
|
inline BoolAdapter GetAdapter(skir_type<bool>);
|
|
1604
1674
|
inline Int32Adapter GetAdapter(skir_type<int32_t>);
|
|
1605
1675
|
inline Int64Adapter GetAdapter(skir_type<int64_t>);
|
|
1606
|
-
inline
|
|
1676
|
+
inline Hash64Adapter GetAdapter(skir_type<uint64_t>);
|
|
1607
1677
|
inline Float32Adapter GetAdapter(skir_type<float>);
|
|
1608
1678
|
inline Float64Adapter GetAdapter(skir_type<double>);
|
|
1609
1679
|
inline TimestampAdapter GetAdapter(skir_type<absl::Time>);
|
|
@@ -2364,7 +2434,7 @@ skir::service::HttpHeaders HttplibToSkirHeaders(const HttplibHeaders& input) {
|
|
|
2364
2434
|
|
|
2365
2435
|
template <typename MethodsTuple, std::size_t... Indices>
|
|
2366
2436
|
constexpr bool unique_method_numbers_impl(std::index_sequence<Indices...>) {
|
|
2367
|
-
constexpr std::array<
|
|
2437
|
+
constexpr std::array<int64_t, sizeof...(Indices)> numbers = {
|
|
2368
2438
|
std::get<Indices>(MethodsTuple()).kNumber...};
|
|
2369
2439
|
for (std::size_t i = 0; i < sizeof...(Indices); ++i) {
|
|
2370
2440
|
for (std::size_t j = i + 1; j < sizeof...(Indices); ++j) {
|
|
@@ -2593,33 +2663,19 @@ struct MethodListAdapter {
|
|
|
2593
2663
|
inline MethodDescriptorAdapter GetAdapter(skir_type<MethodDescriptor>);
|
|
2594
2664
|
inline MethodListAdapter GetAdapter(skir_type<MethodList>);
|
|
2595
2665
|
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
<
|
|
2599
|
-
<head>
|
|
2600
|
-
<meta charset="utf-8" />
|
|
2601
|
-
<title>RESTudio</title>
|
|
2602
|
-
<script src="https://cdn.jsdelivr.net/npm/restudio/dist/restudio-standalone.js"></script>
|
|
2603
|
-
</head>
|
|
2604
|
-
<body style="margin: 0; padding: 0;">
|
|
2605
|
-
<restudio-app></restudio-app>
|
|
2606
|
-
</body>
|
|
2607
|
-
</html>
|
|
2608
|
-
)html";
|
|
2609
|
-
|
|
2610
|
-
template <typename ServiceImpl, typename RequestMeta, typename ResponseMeta>
|
|
2666
|
+
std::string GetStudioHtml(absl::string_view studio_app_js_url);
|
|
2667
|
+
|
|
2668
|
+
template <typename ServiceImpl, typename RequestMeta>
|
|
2611
2669
|
class HandleRequestOp {
|
|
2612
2670
|
public:
|
|
2613
2671
|
HandleRequestOp(ServiceImpl* absl_nonnull service_impl,
|
|
2614
2672
|
absl::string_view request_body,
|
|
2615
|
-
skir::
|
|
2616
|
-
const RequestMeta* absl_nonnull request_meta
|
|
2617
|
-
ResponseMeta* absl_nonnull response_meta)
|
|
2673
|
+
const skir::service::ServiceOptions* absl_nonnull options,
|
|
2674
|
+
const RequestMeta* absl_nonnull request_meta)
|
|
2618
2675
|
: service_impl_(*service_impl),
|
|
2619
2676
|
request_body_(request_body),
|
|
2620
|
-
|
|
2621
|
-
request_meta_(*request_meta)
|
|
2622
|
-
response_meta_(*response_meta) {}
|
|
2677
|
+
options_(*options),
|
|
2678
|
+
request_meta_(*request_meta) {}
|
|
2623
2679
|
|
|
2624
2680
|
skir::service::RawResponse Run() {
|
|
2625
2681
|
if (request_body_ == "" || request_body_ == "list") {
|
|
@@ -2631,20 +2687,16 @@ class HandleRequestOp {
|
|
|
2631
2687
|
typename ServiceImpl::methods());
|
|
2632
2688
|
ReadableJson json;
|
|
2633
2689
|
MethodListAdapter::Append(method_list, json);
|
|
2634
|
-
return
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
} else if (request_body_ == "debug" || request_body_ == "restudio") {
|
|
2639
|
-
return {std::string(kRestudioHtml), skir::service::ResponseType::kOkHtml};
|
|
2690
|
+
return skir_internal::MakeOkJsonResponse(json.out);
|
|
2691
|
+
} else if (request_body_ == "studio") {
|
|
2692
|
+
return skir_internal::MakeOkHtmlResponse(
|
|
2693
|
+
GetStudioHtml(options_.studio_app_js_url));
|
|
2640
2694
|
}
|
|
2641
2695
|
|
|
2642
2696
|
if (const absl::Status status = request_body_parsed_.Parse(request_body_);
|
|
2643
2697
|
!status.ok()) {
|
|
2644
|
-
return
|
|
2645
|
-
absl::StrCat("bad request: ", status.message())
|
|
2646
|
-
skir::service::ResponseType::kBadRequest,
|
|
2647
|
-
};
|
|
2698
|
+
return skir_internal::MakeBadRequestResponse(
|
|
2699
|
+
absl::StrCat("bad request: ", status.message()));
|
|
2648
2700
|
}
|
|
2649
2701
|
|
|
2650
2702
|
std::apply(
|
|
@@ -2655,19 +2707,16 @@ class HandleRequestOp {
|
|
|
2655
2707
|
if (raw_response_.has_value()) {
|
|
2656
2708
|
return std::move(*raw_response_);
|
|
2657
2709
|
}
|
|
2658
|
-
return
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
"; number: ", request_body_parsed_.method_number.value_or(-1)),
|
|
2662
|
-
skir::service::ResponseType::kBadRequest};
|
|
2710
|
+
return skir_internal::MakeBadRequestResponse(absl::StrCat(
|
|
2711
|
+
"bad request: method not found: ", request_body_parsed_.method_name,
|
|
2712
|
+
"; number: ", request_body_parsed_.method_number.value_or(-1)));
|
|
2663
2713
|
}
|
|
2664
2714
|
|
|
2665
2715
|
private:
|
|
2666
2716
|
ServiceImpl& service_impl_;
|
|
2667
2717
|
const absl::string_view request_body_;
|
|
2668
|
-
const skir::
|
|
2718
|
+
const skir::service::ServiceOptions& options_;
|
|
2669
2719
|
const RequestMeta& request_meta_;
|
|
2670
|
-
ResponseMeta& response_meta_;
|
|
2671
2720
|
|
|
2672
2721
|
RequestBody request_body_parsed_;
|
|
2673
2722
|
|
|
@@ -2689,29 +2738,28 @@ class HandleRequestOp {
|
|
|
2689
2738
|
// an error in this case.
|
|
2690
2739
|
return;
|
|
2691
2740
|
}
|
|
2692
|
-
raw_response_.emplace();
|
|
2693
2741
|
absl::StatusOr<RequestType> request = skir::Parse<RequestType>(
|
|
2694
|
-
request_body_parsed_.request_data,
|
|
2742
|
+
request_body_parsed_.request_data, options_.unrecognized_values);
|
|
2695
2743
|
if (!request.ok()) {
|
|
2696
|
-
raw_response_
|
|
2697
|
-
absl::StrCat("bad request: ", request.status().message());
|
|
2698
|
-
raw_response_->type = skir::service::ResponseType::kBadRequest;
|
|
2744
|
+
raw_response_ = skir_internal::MakeBadRequestResponse(
|
|
2745
|
+
absl::StrCat("bad request: ", request.status().message()));
|
|
2699
2746
|
return;
|
|
2700
2747
|
}
|
|
2701
|
-
|
|
2702
|
-
method, std::move(*request), request_meta_
|
|
2703
|
-
if (!
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2748
|
+
skir::service::ErrorOr<ResponseType> response_or_error =
|
|
2749
|
+
service_impl_(method, std::move(*request), request_meta_);
|
|
2750
|
+
if (!response_or_error.ok()) {
|
|
2751
|
+
auto& error = std::get<skir::service::Error>(response_or_error.variant());
|
|
2752
|
+
raw_response_ = skir_internal::MakeServerErrorResponse(
|
|
2753
|
+
std::move(error.message), int(error.code));
|
|
2707
2754
|
return;
|
|
2708
2755
|
}
|
|
2756
|
+
const auto& response = std::get<ResponseType>(response_or_error.variant());
|
|
2709
2757
|
if (request_body_parsed_.readable) {
|
|
2710
|
-
raw_response_
|
|
2711
|
-
|
|
2758
|
+
raw_response_ =
|
|
2759
|
+
skir_internal::MakeOkJsonResponse(skir::ToReadableJson(response));
|
|
2712
2760
|
} else {
|
|
2713
|
-
raw_response_
|
|
2714
|
-
|
|
2761
|
+
raw_response_ =
|
|
2762
|
+
skir_internal::MakeOkJsonResponse(skir::ToDenseJson(response));
|
|
2715
2763
|
}
|
|
2716
2764
|
}
|
|
2717
2765
|
};
|
|
@@ -2724,8 +2772,7 @@ class HttplibClient : public skir::service::Client {
|
|
|
2724
2772
|
|
|
2725
2773
|
absl::StatusOr<std::string> operator()(
|
|
2726
2774
|
absl::string_view request_data,
|
|
2727
|
-
const skir::service::HttpHeaders& request_headers
|
|
2728
|
-
skir::service::HttpHeaders& response_headers) const {
|
|
2775
|
+
const skir::service::HttpHeaders& request_headers) const {
|
|
2729
2776
|
auto headers =
|
|
2730
2777
|
decltype(std::declval<HttplibClientPtr>()->Get("")->headers)();
|
|
2731
2778
|
SkirToHttplibHeaders(request_headers, headers);
|
|
@@ -2733,7 +2780,6 @@ class HttplibClient : public skir::service::Client {
|
|
|
2733
2780
|
client_->Post(query_path_, headers, request_data.data(),
|
|
2734
2781
|
request_data.length(), "text/plain; charset=utf-8");
|
|
2735
2782
|
if (result) {
|
|
2736
|
-
response_headers = HttplibToSkirHeaders(result->headers);
|
|
2737
2783
|
const int status_code = result->status;
|
|
2738
2784
|
if (200 <= status_code && status_code <= 299) {
|
|
2739
2785
|
// OK status.
|
|
@@ -2751,7 +2797,6 @@ class HttplibClient : public skir::service::Client {
|
|
|
2751
2797
|
}
|
|
2752
2798
|
} else {
|
|
2753
2799
|
// HTTP error.
|
|
2754
|
-
response_headers = {};
|
|
2755
2800
|
std::stringstream ss;
|
|
2756
2801
|
ss << "HTTP error: " << result.error();
|
|
2757
2802
|
return {absl::UnknownError(ss.str())};
|
|
@@ -2775,11 +2820,15 @@ namespace service {
|
|
|
2775
2820
|
// 1. It must have a public `methods` type alias which resolves to a tuple of
|
|
2776
2821
|
// // method types.
|
|
2777
2822
|
// 2. For each method, it must have a member function with this signature:
|
|
2823
|
+
//
|
|
2778
2824
|
// absl::StatusOr<typename Method::response_type> operator()(
|
|
2779
2825
|
// Method method,
|
|
2780
2826
|
// typename Method::request_type request,
|
|
2781
|
-
// const HttpHeaders& request_headers
|
|
2782
|
-
//
|
|
2827
|
+
// const HttpHeaders& request_headers);
|
|
2828
|
+
//
|
|
2829
|
+
// Or, if you want to specify a specific HTTP status code other than 500
|
|
2830
|
+
// in case of an error, use ErrorOr<typename Method::response_type>
|
|
2831
|
+
// instead of absl::StatusOr<typename Method::response_type>.
|
|
2783
2832
|
//
|
|
2784
2833
|
// For example:
|
|
2785
2834
|
//
|
|
@@ -2792,16 +2841,14 @@ namespace service {
|
|
|
2792
2841
|
// absl::StatusOr<skirout_methods::ListUsersResponse> operator()(
|
|
2793
2842
|
// skirout_methods::ListUsers,
|
|
2794
2843
|
// skirout_methods::ListUsersRequest request,
|
|
2795
|
-
// const HttpHeaders& request_headers
|
|
2796
|
-
// HttpHeaders& response_headers) const {
|
|
2844
|
+
// const HttpHeaders& request_headers) const {
|
|
2797
2845
|
// ...
|
|
2798
2846
|
// }
|
|
2799
2847
|
//
|
|
2800
2848
|
// absl::StatusOr<skirout_methods::GetUserResponse> operator()(
|
|
2801
2849
|
// skirout_methods::GetUser,
|
|
2802
2850
|
// skirout_methods::GetUserRequest request,
|
|
2803
|
-
// const HttpHeaders& request_headers
|
|
2804
|
-
// HttpHeaders& response_headers) const {
|
|
2851
|
+
// const HttpHeaders& request_headers) const {
|
|
2805
2852
|
// ...
|
|
2806
2853
|
// }
|
|
2807
2854
|
// };
|
|
@@ -2815,20 +2862,14 @@ namespace service {
|
|
|
2815
2862
|
// If the request is a GET request, pass in the decoded query string as the
|
|
2816
2863
|
// request's body. The query string is the part of the URL after '?', and it can
|
|
2817
2864
|
// be decoded with DecodeUrlQueryString.
|
|
2818
|
-
//
|
|
2819
|
-
// Pass in UnrecognizedValuesPolicy::kKeep if the request is guaranteed to come
|
|
2820
|
-
// from a trusted user.
|
|
2821
2865
|
template <typename ServiceImpl>
|
|
2822
2866
|
RawResponse HandleRequest(ServiceImpl& service_impl,
|
|
2823
2867
|
absl::string_view request_body,
|
|
2824
2868
|
const HttpHeaders& request_headers,
|
|
2825
|
-
|
|
2826
|
-
UnrecognizedValuesPolicy unrecognized_values =
|
|
2827
|
-
UnrecognizedValuesPolicy::kDrop) {
|
|
2869
|
+
const ServiceOptions& options = {}) {
|
|
2828
2870
|
skir_internal::assert_unique_method_numbers<typename ServiceImpl::methods>();
|
|
2829
|
-
return skir_internal::HandleRequestOp(&service_impl, request_body,
|
|
2830
|
-
|
|
2831
|
-
&response_headers)
|
|
2871
|
+
return skir_internal::HandleRequestOp(&service_impl, request_body, &options,
|
|
2872
|
+
&request_headers)
|
|
2832
2873
|
.Run();
|
|
2833
2874
|
}
|
|
2834
2875
|
|
|
@@ -2844,21 +2885,16 @@ absl::StatusOr<std::string> DecodeUrlQueryString(
|
|
|
2844
2885
|
//
|
|
2845
2886
|
// ServiceImpl must satisfy the requirements outlined in the documentation for
|
|
2846
2887
|
// HandleRequest.
|
|
2847
|
-
//
|
|
2848
|
-
// Pass in UnrecognizedValuesPolicy::kKeep if the request is guaranteed to come
|
|
2849
|
-
// from a trusted user.
|
|
2850
2888
|
template <typename HttplibServer, typename ServiceImpl>
|
|
2851
|
-
void InstallServiceOnHttplibServer(
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
UnrecognizedValuesPolicy::kDrop) {
|
|
2889
|
+
void InstallServiceOnHttplibServer(HttplibServer& server,
|
|
2890
|
+
absl::string_view query_path,
|
|
2891
|
+
std::shared_ptr<ServiceImpl> service_impl,
|
|
2892
|
+
const ServiceOptions& options = {}) {
|
|
2856
2893
|
ABSL_CHECK_NE(service_impl, nullptr);
|
|
2857
2894
|
const typename HttplibServer::Handler handler = //
|
|
2858
|
-
[service_impl,
|
|
2895
|
+
[service_impl, options](const auto& req, auto& resp) {
|
|
2859
2896
|
const HttpHeaders request_headers =
|
|
2860
2897
|
skir_internal::HttplibToSkirHeaders(req.headers);
|
|
2861
|
-
HttpHeaders response_headers;
|
|
2862
2898
|
absl::string_view request_body;
|
|
2863
2899
|
std::string decoded_query_string;
|
|
2864
2900
|
if (!req.body.empty()) {
|
|
@@ -2874,14 +2910,12 @@ void InstallServiceOnHttplibServer(
|
|
|
2874
2910
|
DecodeUrlQueryString(query_string).value_or("");
|
|
2875
2911
|
request_body = decoded_query_string;
|
|
2876
2912
|
}
|
|
2877
|
-
RawResponse raw_response =
|
|
2878
|
-
|
|
2879
|
-
response_headers, unrecognized_values);
|
|
2880
|
-
skir_internal::SkirToHttplibHeaders(response_headers, resp.headers);
|
|
2913
|
+
RawResponse raw_response = HandleRequest(*service_impl, request_body,
|
|
2914
|
+
request_headers, options);
|
|
2881
2915
|
|
|
2882
2916
|
resp.set_content(std::move(raw_response.data),
|
|
2883
|
-
std::string(raw_response.content_type
|
|
2884
|
-
resp.status = raw_response.status_code
|
|
2917
|
+
std::string(raw_response.content_type));
|
|
2918
|
+
resp.status = raw_response.status_code;
|
|
2885
2919
|
};
|
|
2886
2920
|
server.Get(std::string(query_path), handler);
|
|
2887
2921
|
server.Post(std::string(query_path), handler);
|
|
@@ -2894,16 +2928,11 @@ template <typename Method>
|
|
|
2894
2928
|
absl::StatusOr<typename Method::response_type> InvokeRemote(
|
|
2895
2929
|
const Client& client, Method method,
|
|
2896
2930
|
const typename Method::request_type& request,
|
|
2897
|
-
const HttpHeaders& request_headers = {}
|
|
2898
|
-
HttpHeaders* absl_nonnull response_headers = nullptr) {
|
|
2931
|
+
const HttpHeaders& request_headers = {}) {
|
|
2899
2932
|
const std::string request_data = absl::StrCat(
|
|
2900
2933
|
Method::kMethodName, ":", Method::kNumber, "::", ToDenseJson(request));
|
|
2901
|
-
HttpHeaders response_headers_tmp;
|
|
2902
2934
|
absl::StatusOr<std::string> response_data =
|
|
2903
|
-
client(request_data, request_headers
|
|
2904
|
-
if (response_headers != nullptr) {
|
|
2905
|
-
*response_headers = std::move(response_headers_tmp);
|
|
2906
|
-
}
|
|
2935
|
+
client(request_data, request_headers);
|
|
2907
2936
|
if (!response_data.ok()) {
|
|
2908
2937
|
return std::move(response_data).status();
|
|
2909
2938
|
}
|
package/client/skir.testing.h
CHANGED
|
@@ -252,10 +252,9 @@ class ClientForTesting : public ::skir::service::Client {
|
|
|
252
252
|
|
|
253
253
|
absl::StatusOr<std::string> operator()(
|
|
254
254
|
absl::string_view request_data,
|
|
255
|
-
const skir::service::HttpHeaders& request_headers
|
|
256
|
-
skir::service::HttpHeaders& response_headers) const override {
|
|
255
|
+
const skir::service::HttpHeaders& request_headers) const override {
|
|
257
256
|
return ::skir::service::HandleRequest(api_impl_, request_data,
|
|
258
|
-
request_headers
|
|
257
|
+
request_headers)
|
|
259
258
|
.AsStatus();
|
|
260
259
|
}
|
|
261
260
|
|
package/dist/enum_variant.js
CHANGED
|
@@ -17,7 +17,7 @@ export function getEnumVariants(variants, typeSpeller) {
|
|
|
17
17
|
}
|
|
18
18
|
function makeUnknownVariant() {
|
|
19
19
|
return {
|
|
20
|
-
variantName: "
|
|
20
|
+
variantName: "UNKNOWN",
|
|
21
21
|
valueType: "",
|
|
22
22
|
valueTypeWithNamespace: "",
|
|
23
23
|
variantNumber: 0,
|
|
@@ -74,7 +74,7 @@ function usePointer(type) {
|
|
|
74
74
|
case "bool":
|
|
75
75
|
case "int32":
|
|
76
76
|
case "int64":
|
|
77
|
-
case "
|
|
77
|
+
case "hash64":
|
|
78
78
|
case "float32":
|
|
79
79
|
case "float64":
|
|
80
80
|
case "timestamp":
|
package/dist/enum_variant.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enum_variant.js","sourceRoot":"","sources":["../src/enum_variant.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,WAAW,EAAE,MAAM,eAAe,CAAC;AA+BtE,MAAM,UAAU,eAAe,CAC7B,QAA0B,EAC1B,WAAwB;IAExB,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAClC,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,IAAI,UAA8B,CAAC;QACnC,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACnB,UAAU,GAAG,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;QACvE,CAAC;QACD,UAAU,CAAC,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO;QACL,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"enum_variant.js","sourceRoot":"","sources":["../src/enum_variant.ts"],"names":[],"mappings":"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"}
|
package/dist/type_speller.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skir-cc-gen",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"lint:fix": "eslint src/**/*.ts --fix"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"skir-internal": "^0.0
|
|
42
|
+
"skir-internal": "^0.1.0",
|
|
43
43
|
"zod": "^4.2.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"mocha": "^11.7.5",
|
|
54
54
|
"prettier": "^3.2.4",
|
|
55
55
|
"prettier-plugin-organize-imports": "^4.2.0",
|
|
56
|
-
"skir": "^
|
|
56
|
+
"skir": "^1.0.11",
|
|
57
57
|
"ts-node": "^10.9.2",
|
|
58
58
|
"tsx": "^4.21.0",
|
|
59
59
|
"typescript": "^5.2.2",
|
package/src/enum_variant.ts
CHANGED
|
@@ -50,7 +50,7 @@ export function getEnumVariants(
|
|
|
50
50
|
|
|
51
51
|
function makeUnknownVariant(): MutableEnumVariant {
|
|
52
52
|
return {
|
|
53
|
-
variantName: "
|
|
53
|
+
variantName: "UNKNOWN",
|
|
54
54
|
valueType: "",
|
|
55
55
|
valueTypeWithNamespace: "",
|
|
56
56
|
variantNumber: 0,
|
|
@@ -116,7 +116,7 @@ function usePointer(type: ResolvedType): boolean {
|
|
|
116
116
|
case "bool":
|
|
117
117
|
case "int32":
|
|
118
118
|
case "int64":
|
|
119
|
-
case "
|
|
119
|
+
case "hash64":
|
|
120
120
|
case "float32":
|
|
121
121
|
case "float64":
|
|
122
122
|
case "timestamp":
|