Skip to content

Commit f625619

Browse files
authored
Merge pull request #200 from open-watt/system_commands
Add some system info commands
2 parents 1b17396 + 95fb8be commit f625619

File tree

5 files changed

+172
-19
lines changed

5 files changed

+172
-19
lines changed

src/manager/console/argument.d

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import urt.meta.nullable;
88
import urt.si.quantity;
99
import urt.si.unit : ScaledUnit;
1010
import urt.string;
11+
import urt.time : Duration;
1112
import urt.traits;
1213
import urt.variant;
1314

@@ -177,6 +178,22 @@ const(char[]) convertVariant(T)(ref const Variant v, out T r) nothrow @nogc
177178
}
178179
}
179180

181+
const(char[]) convertVariant(ref const Variant v, out Duration r) nothrow @nogc
182+
{
183+
if (v.isDuration)
184+
r = v.asDuration;
185+
else if (v.isString)
186+
{
187+
const(char)[] s = v.asString;
188+
ptrdiff_t taken = r.fromString(s);
189+
if (taken != s.length)
190+
return "Invalid duration value";
191+
}
192+
else
193+
return "Invalid duration value";
194+
return null;
195+
}
196+
180197
const(char[]) convertVariant(T)(ref const Variant v, out T r) nothrow @nogc
181198
if (is(const(char)[] : T))
182199
{

src/manager/console/function_command.d

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ public import manager.console.session;
1818
public import manager.expression : NamedArgument;
1919

2020

21+
// UDA to attach custom tab completion to a command function
22+
struct TabComplete
23+
{
24+
Array!String function(bool is_value, const(char)[] name, const(char)[] value) nothrow @nogc suggest;
25+
}
26+
27+
2128
// uses GC
2229
char[] transformCommandName(const(char)[] name)
2330
{
@@ -30,6 +37,7 @@ char[] transformCommandName(const(char)[] name)
3037
}
3138
return result;
3239
}
40+
enum TransformCommandName(const(char)[] name) = transformCommandName(name);
3341

3442
nothrow @nogc:
3543

@@ -104,14 +112,14 @@ nothrow @nogc:
104112

105113
FunctionCommand fnCmd = console.m_allocator.allocT!FunctionCommand(console, commandName ? commandName.makeString(defaultAllocator) : StringLit!FunctionName, cast(void*)i, &functionAdapter);
106114

107-
alias ParamNames = parameter_identifier_tuple!fun[1 .. $];
115+
alias ParamNames = STATIC_MAP!(TransformCommandName, parameter_identifier_tuple!fun[1 .. $]);
108116
alias Params = STATIC_MAP!(Unqual, Parameters!fun[1 .. $]);
109117

110118
static foreach (j; 0 .. ParamNames.length)
111119
{
112-
static if (ParamNames[j] != "args")
120+
static if (ParamNames[j] != "args" && ParamNames[j] != "named_args")
113121
{{
114-
fnCmd.args ~= FunctionArgument(StringLit!(ParamNames[j].transformCommandName()));
122+
fnCmd.args ~= FunctionArgument(StringLit!(ParamNames[j]));
115123
static if (is(Params[j] == Nullable!T, T))
116124
alias ArgTy = T;
117125
else
@@ -121,6 +129,13 @@ nothrow @nogc:
121129
}}
122130
}
123131

132+
// Check for TabComplete UDA on the function
133+
static foreach (attr; __traits(getAttributes, fun))
134+
{
135+
static if (is(typeof(attr) == TabComplete))
136+
fnCmd.custom_suggest = attr.suggest;
137+
}
138+
124139
return fnCmd;
125140
}
126141

@@ -211,20 +226,30 @@ private:
211226
void* instance;
212227
GenericCall fn;
213228
Array!FunctionArgument args;
229+
Array!String function(bool, const(char)[], const(char)[]) nothrow @nogc custom_suggest;
214230

215-
Array!String suggestArgs(const(char)[] argPrefix)
231+
Array!String suggestArgs(const(char)[] arg_prefix)
216232
{
217233
Array!String suggestions;
234+
if (custom_suggest !is null)
235+
suggestions = custom_suggest(false, arg_prefix, null);
218236
foreach (ref arg; args)
219237
{
220-
if (arg.name.startsWith(argPrefix))
238+
if (arg.name.startsWith(arg_prefix))
221239
suggestions ~= String(MutableString!0(Concat, arg.name, '=')); // TODO: MOVE construct!
222240
}
223241
return suggestions;
224242
}
225243

226244
Array!String suggestValues(const(char)[] argument, const(char)[] value)
227245
{
246+
if (custom_suggest !is null)
247+
{
248+
Array!String suggestions = custom_suggest(true, argument, value);
249+
if (suggestions.length > 0)
250+
return suggestions;
251+
}
252+
228253
foreach (ref arg; args)
229254
{
230255
if (arg.name[] == argument[])
@@ -253,7 +278,7 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
253278
import urt.meta;
254279

255280
alias Params = STATIC_MAP!(Unqual, Parameters!F[1 .. $]);
256-
alias ParamNames = parameter_identifier_tuple!F[1 .. $];
281+
alias ParamNames = STATIC_MAP!(TransformCommandName, parameter_identifier_tuple!F[1 .. $]);
257282

258283
Tuple!Params params;
259284
error = null;
@@ -265,15 +290,18 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
265290
{
266291
static foreach (i, P; Params)
267292
{
268-
case Alias!(transformCommandName(ParamNames[i])):
269-
error = convertVariant(param.value, params[i]);
270-
if (error)
271-
{
272-
error = tconcat("Argument '", param.name, "' error: ", error);
273-
break outer;
274-
}
275-
gotArg[i] = true;
276-
break param_switch;
293+
static if (ParamNames[i] != "args" && ParamNames[i] != "named-args")
294+
{
295+
case ParamNames[i]:
296+
error = convertVariant(param.value, params[i]);
297+
if (error)
298+
{
299+
error = tconcat("Argument '", param.name, "' error: ", error);
300+
break outer;
301+
}
302+
gotArg[i] = true;
303+
break param_switch;
304+
}
277305
}
278306
default:
279307
error = tconcat("Unknown parameter '", param.name, "'");
@@ -284,16 +312,21 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
284312
static foreach (i, P; Params)
285313
{
286314
{
287-
static if (transformCommandName(ParamNames[i]) == "args")
315+
static if (ParamNames[i] == "args")
288316
{
289-
static assert(is(P == Variant[]), "`args` parameter must be of type Variant[]");
317+
static assert(is(const(Variant)[] : P), "`args` parameter must be of type const(Variant)[]");
290318
params[i] = args;
291319
}
320+
else static if (ParamNames[i] == "named-args")
321+
{
322+
static assert(is(const(NamedArgument)[] : P), "`named_args` parameter must be of type const(NamedArgument)[]");
323+
params[i] = parameters;
324+
}
292325
else static if (!is(P : Nullable!U, U))
293326
{
294327
if (!gotArg[i])
295328
{
296-
error = tconcat("Missing argument: ", Alias!(transformCommandName(ParamNames[i])));
329+
error = tconcat("Missing argument: ", ParamNames[i]);
297330
goto done;
298331
}
299332
}

src/manager/package.d

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ nothrow @nogc:
9292

9393
console.registerCommand!log_level("/system", this);
9494
console.registerCommand!set_hostname("/system", this);
95+
console.registerCommand!get_hostname("/system", this, "hostname");
9596
console.registerCommand!set_update_rate("/system", this, "update-rate");
97+
console.registerCommand!uptime("/system", this);
98+
console.registerCommand!sysinfo("/system", this);
99+
console.registerCommand!show_time("/system", this, "time");
96100

97101
console.registerCommand!device_print("/device", this, "print");
98102

src/manager/system.d

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
module manager.system;
22

3+
import urt.array;
34
import urt.log;
45
import urt.mem.allocator;
56
import urt.string;
7+
import urt.system;
8+
import urt.time;
69

710
import manager.console.session;
11+
import manager.console.function_command : TabComplete;
12+
import urt.variant : Variant;
813

914
nothrow @nogc:
1015

@@ -21,3 +26,97 @@ void set_hostname(Session session, const(char)[] hostname)
2126
{
2227
.hostname = hostname.makeString(defaultAllocator());
2328
}
29+
30+
void get_hostname(Session session)
31+
{
32+
session.writeLine(hostname);
33+
}
34+
35+
void uptime(Session session)
36+
{
37+
session.writeLine(getAppTime());
38+
}
39+
40+
Array!String sysinfo_suggest(bool, const(char)[] arg_name, const(char)[]) nothrow @nogc
41+
{
42+
import urt.string : startsWith;
43+
44+
__gshared const String[6] properties = [
45+
StringLit!"hostname",
46+
StringLit!"os",
47+
StringLit!"processor",
48+
StringLit!"total-memory",
49+
StringLit!"available-memory",
50+
StringLit!"uptime"
51+
];
52+
53+
Array!String completions;
54+
foreach (ref prop; properties)
55+
{
56+
if (prop.startsWith(arg_name))
57+
completions ~= prop;
58+
}
59+
return completions;
60+
}
61+
62+
@TabComplete(&sysinfo_suggest)
63+
void sysinfo(Session session, const(Variant)[] args)
64+
{
65+
import urt.string : icmp;
66+
67+
SystemInfo info = get_sysinfo();
68+
69+
if (args.length == 0)
70+
{
71+
session.writeLine("Hostname: ", hostname[]);
72+
session.writeLine("OS: ", info.os_name);
73+
session.writeLine("Processor: ", info.processor);
74+
session.writeLine("Total Memory: ", info.total_memory.format_bytes());
75+
session.writeLine("Available: ", info.available_memory.format_bytes());
76+
session.writeLine("Uptime: ", getAppTime());
77+
}
78+
else foreach (ref arg; args)
79+
{
80+
if (!arg.isString)
81+
{
82+
session.writeLine("Error: Arguments must be property names");
83+
continue;
84+
}
85+
86+
const(char)[] prop = arg.asString;
87+
if (icmp(prop, "hostname") == 0)
88+
session.writeLine(hostname[]);
89+
else if (icmp(prop, "os") == 0)
90+
session.writeLine(info.os_name);
91+
else if (icmp(prop, "processor") == 0)
92+
session.writeLine(info.processor);
93+
else if (icmp(prop, "total-memory") == 0)
94+
session.writeLine(info.total_memory.format_bytes());
95+
else if (icmp(prop, "available-memory") == 0)
96+
session.writeLine(info.available_memory.format_bytes());
97+
else if (icmp(prop, "uptime") == 0)
98+
session.writeLine(getAppTime());
99+
else
100+
session.writeLine("Unknown property: ", prop);
101+
}
102+
}
103+
104+
void show_time(Session session)
105+
{
106+
session.writeLine(getDateTime());
107+
}
108+
109+
// Helper function to format bytes with appropriate unit
110+
private auto format_bytes(ulong bytes) nothrow @nogc
111+
{
112+
import urt.mem.temp : tconcat;
113+
114+
if (bytes < 1024)
115+
return tconcat(bytes, " B");
116+
else if (bytes < 1024 * 1024)
117+
return tconcat(bytes / 1024, " KB");
118+
else if (bytes < 1024 * 1024 * 1024)
119+
return tconcat(bytes / (1024 * 1024), " MB");
120+
else
121+
return tconcat(bytes / (1024 * 1024 * 1024), " GB");
122+
}

0 commit comments

Comments
 (0)