-
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
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
// Copyright 2025 Shota FUJI
//
// Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option.
// You may not use, copy, modify, or distribute this file except according to those terms. You can
// find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License,
// Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version
// 2.0 at <https://www.apache.org/licenses/LICENSE-2.0>
//
// SPDX-License-Identifier: 0BSD OR Apache-2.0
//! This module contains constants and concret type on top of Message struct.
const std = @import("std");
const constants = @import("constants.zig");
const Message = @import("Message.zig");
/// Target UDP port to use for sending IP multicast and broadcast.
/// Though you can also listen for this UDP port to receive broadcast from another client,
/// this library does not provide API for handling that case.
pub const udp_port = 9003;
/// IPv4 address for IP multicast. Send SOOD message to this IP address on `udp_port`.
pub const multicast_ipv4_address = [4]u8{ 239, 255, 90, 90 };
pub const Query = struct {
/// A required property for a discovery query. Node.js API uses hard-coded ID
/// thus this library uses the same one.
pub const query_service_id = Message.Property{
.key = constants.query_service_id_key,
.value = constants.query_service_id_value,
};
pub const properties = [_]Message.Property{
query_service_id,
};
/// Premade bytes for minimum discovery query message.
pub const prebuilt = constants.prebuilt_query;
};
test "Query.prebuilt should equal to one built by `write`" {
const built = try Message.write(std.testing.allocator, .query, &Query.properties);
defer std.testing.allocator.free(built);
try std.testing.expectEqualSlices(u8, built, Query.prebuilt);
}
/// Only subset of fields are stored; everything else is omitted.
/// To access undocumented fields, use `Message.parse` API instead.
pub const Response = struct {
/// TCP port to use for WebSocket.
http_port: u16,
/// Display name of the Roon server.
///
/// This string is what user see in Settings/General page and can be changed at Settings/Setup
/// page.
name: []const u8,
/// Roon server version strings.
///
/// As the name suggests, this value is for display purpose. Format is not defined and there is
/// no stability guarantee, therefore parsing of this value is not recommended.
display_version: []const u8,
/// ID unique to the Roon server.
unique_id: []const u8,
pub const SchemaError = error{
/// Message's type (kind) is not "response" (`R`)
NonResponseKindMessage,
/// Response message does not contain required property.
MissingRequiredProperty,
/// Cannot convert property value to desired type.
UnexpectedPropertyValue,
};
pub const ParseError = SchemaError || Message.HeaderParseError || Message.PropertyParseError;
pub fn parse(bytes: []const u8) ParseError!@This() {
const msg = try Message.parse(bytes);
if (msg.kind != .response) {
return SchemaError.NonResponseKindMessage;
}
var http_port: ?u16 = null;
var name: ?[]const u8 = null;
var display_version: ?[]const u8 = null;
var unique_id: ?[]const u8 = null;
var iter = msg.iterator();
while (try iter.next()) |property| {
if (std.mem.eql(u8, "http_port", property.key)) {
http_port = std.fmt.parseInt(u16, property.value, 10) catch return SchemaError.UnexpectedPropertyValue;
continue;
}
if (std.mem.eql(u8, "name", property.key)) {
name = property.value;
continue;
}
if (std.mem.eql(u8, "display_version", property.key)) {
display_version = property.value;
continue;
}
if (std.mem.eql(u8, "unique_id", property.key)) {
unique_id = property.value;
continue;
}
}
return Response{
.http_port = http_port orelse return SchemaError.MissingRequiredProperty,
.name = name orelse return SchemaError.MissingRequiredProperty,
.display_version = display_version orelse return SchemaError.MissingRequiredProperty,
.unique_id = unique_id orelse return SchemaError.MissingRequiredProperty,
};
}
};