Skip to content

Commit 4f7d9ad

Browse files
Nullability Safety.
1 parent 42797cc commit 4f7d9ad

13 files changed

+199
-188
lines changed

TelnetNegotiationCore.TestClient/MockPipelineClient.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,10 @@ public async Task StartAsync(string address, int port)
6060

6161
while (true)
6262
{
63-
string read = Console.ReadLine() ?? string.Empty;
64-
63+
var read = Console.ReadLine() ?? string.Empty;
6564
if (telnet != null)
6665
{
67-
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
66+
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read) ?? []);
6867
}
6968
}
7069
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Text.Json;
43
using System.Threading.Tasks;
54
using TelnetNegotiationCore.Functional;
@@ -19,112 +18,115 @@ namespace TelnetNegotiationCore.Handlers;
1918
/// </param>
2019
public class MSDPServerHandler(MSDPServerModel model)
2120
{
22-
public MSDPServerModel Data { get; private init; } = model;
21+
/// <summary>
22+
/// Current Data.
23+
/// </summary>
24+
public MSDPServerModel Data { get; private init; } = model;
2325

24-
public async ValueTask HandleAsync(TelnetInterpreter telnet, string clientJson)
25-
{
26-
var json = JsonSerializer.Deserialize<dynamic>(clientJson);
26+
private protected record MSDPModel
27+
{
28+
public string? LIST { get; init; }
29+
public string? REPORT { get; init; }
30+
public string? RESET { get; init; }
31+
public string? SEND { get; init; }
32+
public string? UNREPORT { get; init; }
33+
}
34+
35+
public ValueTask HandleAsync(TelnetInterpreter telnet, string clientJson)
36+
{
37+
var json = JsonSerializer.Deserialize<MSDPModel>(clientJson);
38+
return json switch
39+
{
40+
{ LIST: not null} =>
41+
HandleListRequestAsync(telnet, json.LIST),
42+
{ REPORT: not null} =>
43+
HandleReportRequestAsync(telnet, json.REPORT),
44+
{ RESET: not null} =>
45+
HandleResetRequestAsync(json.RESET),
46+
{ SEND: not null} =>
47+
HandleSendRequestAsync(telnet, json.SEND),
48+
{ UNREPORT: not null} =>
49+
HandleUnReportRequestAsync(json.UNREPORT),
50+
_ => ValueTask.CompletedTask
51+
};
52+
}
2753

28-
if (json.LIST != null)
29-
{
30-
await HandleListRequestAsync(telnet, (string)json.LIST);
31-
}
32-
else if (json.REPORT != null)
33-
{
34-
await HandleReportRequestAsync(telnet, (string)json.REPORT);
35-
}
36-
else if (json.RESET != null)
37-
{
38-
await HandleResetRequestAsync((string)json.RESET);
39-
}
40-
else if (json.SEND != null)
41-
{
42-
await HandleSendRequestAsync(telnet, (string)json.SEND);
43-
}
44-
else if (json.UNREPORT != null)
45-
{
46-
await HandleUnReportRequestAsync((string)json.UNREPORT);
47-
}
54+
/// <summary>
55+
/// Handles a request to LIST a single item. For safety, also supports a list of lists.
56+
/// Should at least support:
57+
/// "COMMANDS" Request an array of commands supported by the server.
58+
/// "LISTS" Request an array of lists supported by the server.
59+
/// "CONFIGURABLE_VARIABLES" Request an array of variables the client can configure.
60+
/// "REPORTABLE_VARIABLES" Request an array of variables the server will report.
61+
/// "REPORTED_VARIABLES" Request an array of variables currently being reported.
62+
/// "SENDABLE_VARIABLES" Request an array of variables the server will send.
63+
/// </summary>
64+
/// <param name="telnet">Telnet Interpreter to callback with.</param>
65+
/// <param name="item">The item to report</param>
66+
private async ValueTask HandleListRequestAsync(TelnetInterpreter telnet, string item) =>
67+
await ExecuteOnAsync(item, async (val) =>
68+
await (Data.Lists.TryGetValue(val, out var value)
69+
? telnet.CallbackNegotiationAsync(
70+
MSDPLibrary.Report(JsonSerializer.Serialize(value()),
71+
telnet.CurrentEncoding))
72+
: ValueTask.CompletedTask));
4873

49-
await Task.CompletedTask;
50-
}
74+
private async ValueTask HandleReportRequestAsync(TelnetInterpreter telnet, string item) =>
75+
await ExecuteOnAsync(item, async (val) =>
76+
{
77+
await HandleSendRequestAsync(telnet, val);
78+
Data.Report(val, async (newVal) => await HandleSendRequestAsync(telnet, newVal));
79+
});
5180

52-
/// <summary>
53-
/// Handles a request to LIST a single item. For safety, also supports a list of lists.
54-
/// Should at least support:
55-
/// "COMMANDS" Request an array of commands supported by the server.
56-
/// "LISTS" Request an array of lists supported by the server.
57-
/// "CONFIGURABLE_VARIABLES" Request an array of variables the client can configure.
58-
/// "REPORTABLE_VARIABLES" Request an array of variables the server will report.
59-
/// "REPORTED_VARIABLES" Request an array of variables currently being reported.
60-
/// "SENDABLE_VARIABLES" Request an array of variables the server will send.
61-
/// </summary>
62-
/// <param name="telnet">Telnet Interpreter to callback with.</param>
63-
/// <param name="item">The item to report</param>
64-
private async ValueTask HandleListRequestAsync(TelnetInterpreter telnet, string item) =>
65-
await ExecuteOnAsync(item, async (val) =>
66-
await (Data.Lists.TryGetValue(val, out var value)
67-
? telnet.CallbackNegotiationAsync(
68-
MSDPLibrary.Report(JsonSerializer.Serialize(value()),
69-
telnet.CurrentEncoding))
70-
: ValueTask.CompletedTask));
81+
/// <summary>
82+
/// The RESET command works like the LIST command, and can be used to reset groups of variables to their initial state.
83+
/// Most commonly RESET will be called with REPORTABLE_VARIABLES or REPORTED_VARIABLES as the argument,
84+
/// though any LIST option can be used.
85+
/// </summary>
86+
/// <param name="item">Item to reset</param>
87+
private async ValueTask HandleResetRequestAsync(string item) =>
88+
await ExecuteOnAsync(item, (var) =>
89+
{
90+
var found = Data.Reportable_Variables().TryGetValue(var, out var list);
91+
return ValueTask.CompletedTask;
92+
});
7193

72-
private async ValueTask HandleReportRequestAsync(TelnetInterpreter telnet, string item) =>
73-
await ExecuteOnAsync(item, async (val) =>
74-
{
75-
await HandleSendRequestAsync(telnet, val);
76-
Data.Report(val, async (newVal) => await HandleSendRequestAsync(telnet, newVal));
77-
});
94+
/// <summary>
95+
/// The SEND command can be used by either side, but should typically be used by the client.
96+
/// After the client has received a list of variables, or otherwise knows which variables exist,
97+
/// it can request the server to send those variables and their values with the SEND command.
98+
/// The value of the SEND command should be a list of variables the client wants returned.
99+
/// </summary>
100+
/// <param name="telnet">Telnet interpreter to send back negotiation with</param>
101+
/// <param name="item">The item to send</param>
102+
private async ValueTask HandleSendRequestAsync(TelnetInterpreter telnet, string item) =>
103+
await ExecuteOnAsync(item, async (var) =>
104+
{
105+
var found = Data.Sendable_Variables().TryGetValue(var, out var val);
106+
var jsonString = $"{{{var}:{(found ? val : "null")}}}";
107+
await telnet.CallbackNegotiationAsync(MSDPLibrary.Report(jsonString, telnet.CurrentEncoding));
108+
});
78109

79-
/// <summary>
80-
/// The RESET command works like the LIST command, and can be used to reset groups of variables to their initial state.
81-
/// Most commonly RESET will be called with REPORTABLE_VARIABLES or REPORTED_VARIABLES as the argument,
82-
/// though any LIST option can be used.
83-
/// </summary>
84-
/// <param name="item">Item to reset</param>
85-
private async ValueTask HandleResetRequestAsync(string item) =>
86-
await ExecuteOnAsync(item, (var) =>
87-
{
88-
var found = Data.Reportable_Variables().TryGetValue(var, out var list);
89-
return ValueTask.CompletedTask;
90-
});
110+
/// <summary>
111+
/// The UNREPORT command is used to remove the report status of variables after the use of the REPORT command.
112+
/// </summary>
113+
/// <param name="item">The item to stop reporting on</param>
114+
private async ValueTask HandleUnReportRequestAsync(string item) =>
115+
await ExecuteOnAsync(item, (var) =>
116+
{
117+
Data.UnReport(var);
118+
return ValueTask.CompletedTask;
119+
});
91120

92-
/// <summary>
93-
/// The SEND command can be used by either side, but should typically be used by the client.
94-
/// After the client has received a list of variables, or otherwise knows which variables exist,
95-
/// it can request the server to send those variables and their values with the SEND command.
96-
/// The value of the SEND command should be a list of variables the client wants returned.
97-
/// </summary>
98-
/// <param name="telnet">Telnet interpreter to send back negotiation with</param>
99-
/// <param name="item">The item to send</param>
100-
private async ValueTask HandleSendRequestAsync(TelnetInterpreter telnet, string item) =>
101-
await ExecuteOnAsync(item, async (var) =>
102-
{
103-
var found = Data.Sendable_Variables().TryGetValue(var, out var val);
104-
var jsonString = $"{{{var}:{(found ? val : "null")}}}";
105-
await telnet.CallbackNegotiationAsync(MSDPLibrary.Report(jsonString, telnet.CurrentEncoding));
106-
});
121+
private async ValueTask ExecuteOnAsync(string item, Func<string, ValueTask> function)
122+
{
123+
var items = item.StartsWith('[')
124+
? JsonSerializer.Deserialize<string[]>(item)
125+
: [item];
107126

108-
/// <summary>
109-
/// The UNREPORT command is used to remove the report status of variables after the use of the REPORT command.
110-
/// </summary>
111-
/// <param name="item">The item to stop reporting on</param>
112-
private async ValueTask HandleUnReportRequestAsync(string item) =>
113-
await ExecuteOnAsync(item, (var) =>
114-
{
115-
Data.UnReport(var);
116-
return ValueTask.CompletedTask;
117-
});
118-
119-
private async ValueTask ExecuteOnAsync(string item, Func<string, ValueTask> function)
120-
{
121-
var items = item.StartsWith('[')
122-
? JsonSerializer.Deserialize<string[]>(item)
123-
: [item];
124-
125-
foreach (var val in items)
126-
{
127-
await function(val);
128-
}
129-
}
130-
}
127+
foreach (var val in items ?? [])
128+
{
129+
await function(val);
130+
}
131+
}
132+
}

TelnetNegotiationCore/Helpers/ListLike.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ namespace LocalMoreLinq
3030

3131
readonly struct ListLike<T>
3232
{
33-
readonly IList<T> rw;
34-
readonly IReadOnlyList<T> ro;
33+
readonly IList<T>? rw;
34+
readonly IReadOnlyList<T>? ro;
3535

3636
public ListLike(IList<T> list)
3737
{

TelnetNegotiationCore/Interpreters/TelnetCharsetInterpreter.cs

+12-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public partial class TelnetInterpreter
2020
/// <summary>
2121
/// Internal Charset Byte State
2222
/// </summary>
23-
private byte[] _charsetByteState;
23+
private byte[] _charsetByteState = [];
2424

2525
/// <summary>
2626
/// Internal Charset Byte Index Value
@@ -30,7 +30,7 @@ public partial class TelnetInterpreter
3030
/// <summary>
3131
/// Internal Accepted Charset Byte State
3232
/// </summary>
33-
private byte[] _acceptedCharsetByteState;
33+
private byte[] _acceptedCharsetByteState = [];
3434

3535
/// <summary>
3636
/// Internal Accepted Charset Byte Index Value
@@ -44,9 +44,9 @@ public partial class TelnetInterpreter
4444
private readonly Func<IEnumerable<EncodingInfo>, IOrderedEnumerable<Encoding>> _charsetOrder = x
4545
=> x.Select(y => y.GetEncoding()).OrderBy(z => z.EncodingName);
4646

47-
private Func<Encoding, ValueTask> SignalCharsetChangeAsync { get; set; }
47+
private Func<Encoding, ValueTask>? SignalCharsetChangeAsync { get; set; }
4848

49-
public Lazy<byte[]> SupportedCharacterSets { get; }
49+
private Lazy<byte[]> SupportedCharacterSets { get; }
5050

5151
/// <summary>
5252
/// Sets the CharacterSet Order
@@ -190,7 +190,7 @@ private void CaptureCharset(OneOf<byte, Trigger> b)
190190
/// <param name="b"></param>
191191
private void CaptureAcceptedCharset(OneOf<byte, Trigger> b)
192192
{
193-
_acceptedCharsetByteState[_acceptedCharsetByteIndex] = b.AsT0;
193+
_acceptedCharsetByteState![_acceptedCharsetByteIndex] = b.AsT0;
194194
_acceptedCharsetByteIndex++;
195195
}
196196

@@ -209,10 +209,13 @@ private async ValueTask CompleteCharsetAsync(StateMachine<State, Trigger>.Transi
209209
var sep = ascii.GetString(_charsetByteState, 0, 1)?[0];
210210
var charsetsOffered = ascii.GetString(_charsetByteState, 1, _charsetByteIndex - 1).Split(sep ?? ' ');
211211

212-
_logger.LogDebug("Charsets offered to us: {@charsetResultDebug}", charsetsOffered);
212+
_logger.LogDebug("Charsets offered to us: {@charsetResultDebug}", [..charsetsOffered]);
213213

214214
var encodingDict = AllowedEncodings().ToDictionary(x => x.GetEncoding().WebName);
215-
var offeredEncodingInfo = charsetsOffered.Select(x => { try { return encodingDict[Encoding.GetEncoding(x).WebName]; } catch { return null; } }).Where(x => x != null);
215+
var offeredEncodingInfo = charsetsOffered
216+
.Select(x => { try { return encodingDict[Encoding.GetEncoding(x).WebName]; } catch { return null; } })
217+
.Where(x => x != null)
218+
.Select(x => x!);
216219
var preferredEncoding = _charsetOrder(offeredEncodingInfo);
217220
var chosenEncoding = preferredEncoding.FirstOrDefault();
218221

@@ -245,11 +248,11 @@ private async ValueTask CompleteAcceptedCharsetAsync(StateMachine<State, Trigger
245248
{
246249
try
247250
{
248-
CurrentEncoding = Encoding.GetEncoding(ascii.GetString(_acceptedCharsetByteState, 0, _acceptedCharsetByteIndex).Trim());
251+
CurrentEncoding = Encoding.GetEncoding(ascii.GetString(_acceptedCharsetByteState!, 0, _acceptedCharsetByteIndex).Trim());
249252
}
250253
catch (Exception ex)
251254
{
252-
_logger.LogError(ex, "Unexpected error during Accepting Charset Negotiation. Could not find charset: {charset}", ascii.GetString(_acceptedCharsetByteState, 0, _acceptedCharsetByteIndex));
255+
_logger.LogError(ex, "Unexpected error during Accepting Charset Negotiation. Could not find charset: {charset}", ascii.GetString(_acceptedCharsetByteState!, 0, _acceptedCharsetByteIndex));
253256
await CallbackNegotiationAsync([(byte)Trigger.IAC, (byte)Trigger.SB, (byte)Trigger.CHARSET, (byte)Trigger.REJECTED, (byte)Trigger.IAC, (byte)Trigger.SE]);
254257
}
255258
_logger.LogInformation("Connection: Accepted Charset Negotiation for: {charset}", CurrentEncoding.WebName);

TelnetNegotiationCore/Interpreters/TelnetEORInterpreter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public partial class TelnetInterpreter
1010
{
1111
private bool? _doEOR = null;
1212

13-
public Func<ValueTask> SignalOnPromptingAsync { get; init; }
13+
public Func<ValueTask>? SignalOnPromptingAsync { get; init; }
1414

1515
/// <summary>
1616
/// Character set Negotiation will set the Character Set and Character Page Server & Client have agreed to.

TelnetNegotiationCore/Interpreters/TelnetGMCPInterpreter.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public partial class TelnetInterpreter
1414
{
1515
private List<byte> _GMCPBytes = [];
1616

17-
public Func<(string Package, string Info), ValueTask> SignalOnGMCPAsync { get; init; }
17+
public Func<(string Package, string Info), ValueTask>? SignalOnGMCPAsync { get; init; }
1818

1919
private StateMachine<State, Trigger> SetupGMCPNegotiation(StateMachine<State, Trigger> tsm)
2020
{
@@ -140,7 +140,7 @@ private async ValueTask CompleteGMCPNegotiation(StateMachine<State, Trigger>.Tra
140140
/// </summary>
141141
/// <param name="_">Transition, ignored.</param>
142142
/// <returns>ValueTask</returns>
143-
private async ValueTask WillGMCPAsync(StateMachine<State, Trigger>.Transition _)
143+
private async ValueTask WillGMCPAsync(StateMachine<State, Trigger>.Transition? _)
144144
{
145145
_logger.LogDebug("Connection: {ConnectionState}", "Announcing the server will GMCP");
146146

TelnetNegotiationCore/Interpreters/TelnetMSDPInterpreter.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ namespace TelnetNegotiationCore.Interpreters;
1818
/// </remarks>
1919
public partial class TelnetInterpreter
2020
{
21-
private List<byte> _currentMSDPInfo;
21+
private List<byte> _currentMSDPInfo = [];
2222

23-
public Func<TelnetInterpreter, string, ValueTask> SignalOnMSDPAsync { get; init; }
23+
public Func<TelnetInterpreter, string, ValueTask>? SignalOnMSDPAsync { get; init; }
2424

2525
/// <summary>
2626
/// Mud Server Status Protocol will provide information to the requestee about the server's contents.

0 commit comments

Comments
 (0)