-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
// Copyright 2025 Shota FUJI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
namespace Moo {
public errordomain MetadataParseError {
NOT_A_MOO_MESSAGE,
UNSUPPORTED_VERSION,
UNSUPPORTED_SERVICE_NAME,
}
private const string SIGNATURE = "MOO/";
public class Metadata : Object {
public uint32 version { get; construct; }
public string verb { get; construct; }
public string service { get; construct; }
public int last_byte_index { get; construct; default = -1; }
public Metadata(string verb, string service) {
Object(version: 1, verb: verb, service: service);
}
public Metadata.from_string(string src) throws MetadataParseError {
int i = 0;
if (src.index_of(SIGNATURE) != 0) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Signature not found.");
}
i = SIGNATURE.length;
// Parse version field
var space_after_version = src.index_of_char(' ', i);
if (space_after_version < 0 || space_after_version == i) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Missing version field.");
}
uint version;
if (!uint.try_parse(src.slice((long) i, (long) space_after_version), out version, null, 10)) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Version is not a valid number.");
}
if (version == 0) {
throw new MetadataParseError.UNSUPPORTED_VERSION("0 is not a valid version.");
}
if (version == 0 || version >= uint32.MAX) {
throw new MetadataParseError.UNSUPPORTED_VERSION("Version does not fit in 32bit unsinged integer range.");
}
i = space_after_version + 1;
// Parse verb (method) field
var space_after_verb = src.index_of_char(' ', i);
if (space_after_verb < 0 || space_after_verb == i) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Missing verb field.");
}
string verb = src.slice((long) i, (long) space_after_verb);
foreach (var byte in verb.to_utf8()) {
if (!byte.isupper()) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Verb contains non ASCII upper case letter.");
}
}
i = space_after_verb + 1;
// Parse service field
var next_lf = src.index_of_char('\n', i);
if (next_lf < 0 || next_lf == i) {
throw new MetadataParseError.NOT_A_MOO_MESSAGE("Missing service field.");
}
string service = src.slice((long) i, (long) next_lf);
if (!service.validate()) {
throw new MetadataParseError.UNSUPPORTED_SERVICE_NAME("Service name is not a valid UTF8 text.");
}
i = next_lf + 1;
Object(version: (uint32) version, verb: verb, service: service, last_byte_index: i);
}
public string to_string() {
return "%s%u %s %s\n".printf(SIGNATURE, version, verb, service);
}
}
}