Fix json output with invalid unicode.
This commit is contained in:
parent
b7f616a945
commit
419b99c277
|
@ -34,11 +34,6 @@ pub fn build(b: *Builder) void {
|
|||
"weather_location",
|
||||
"weather_location",
|
||||
) orelse "";
|
||||
if (weather_location.len == 0) {
|
||||
weather_location = "\"\"";
|
||||
} else if (weather_location[0] != '"') {
|
||||
weather_location = std.fmt.allocPrint(b.allocator, "\"{}\"", .{weather_location}) catch "\"\"";
|
||||
}
|
||||
exe.addBuildOption([]const u8, "weather_location", weather_location);
|
||||
|
||||
//exe.strip = true;
|
||||
|
|
67
build_runner.zig
Normal file
67
build_runner.zig
Normal file
|
@ -0,0 +1,67 @@
|
|||
const root = @import("build.zig");
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const fmt = std.fmt;
|
||||
const Builder = std.build.Builder;
|
||||
const Pkg = std.build.Pkg;
|
||||
const InstallArtifactStep = std.build.InstallArtifactStep;
|
||||
const LibExeObjStep = std.build.LibExeObjStep;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
///! This is a modified build runner to extract information out of build.zig
|
||||
///! Modified from the std.special.build_runner
|
||||
pub fn main() !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const allocator = &arena.allocator;
|
||||
|
||||
const builder = try Builder.create(allocator, "", "", "");
|
||||
defer builder.destroy();
|
||||
|
||||
try runBuild(builder);
|
||||
|
||||
const stdout_stream = io.getStdOut().outStream();
|
||||
|
||||
// TODO: We currently add packages from every LibExeObj step that the install step depends on.
|
||||
// Should we error out or keep one step or something similar?
|
||||
// We also flatten them, we should probably keep the nested structure.
|
||||
for (builder.top_level_steps.items) |tls| {
|
||||
for (tls.step.dependencies.items) |step| {
|
||||
try processStep(stdout_stream, step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn processStep(stdout_stream: anytype, step: *std.build.Step) anyerror!void {
|
||||
if (step.cast(InstallArtifactStep)) |install_exe| {
|
||||
for (install_exe.artifact.packages.items) |pkg| {
|
||||
try processPackage(stdout_stream, pkg);
|
||||
}
|
||||
} else if (step.cast(LibExeObjStep)) |exe| {
|
||||
for (exe.packages.items) |pkg| {
|
||||
try processPackage(stdout_stream, pkg);
|
||||
}
|
||||
} else {
|
||||
for (step.dependencies.items) |unknown_step| {
|
||||
try processStep(stdout_stream, unknown_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn processPackage(out_stream: anytype, pkg: Pkg) anyerror!void {
|
||||
try out_stream.print("{}\x00{}\n", .{ pkg.name, pkg.path });
|
||||
if (pkg.dependencies) |dependencies| {
|
||||
for (dependencies) |dep| {
|
||||
try processPackage(out_stream, dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn runBuild(builder: *Builder) anyerror!void {
|
||||
switch (@typeInfo(@TypeOf(root.build).ReturnType)) {
|
||||
.Void => root.build(builder),
|
||||
.ErrorUnion => try root.build(builder),
|
||||
else => @compileError("expected return type of build to be 'void' or '!void'"),
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ const log = std.log;
|
|||
const terminal_version = @import("build_options").terminal_version;
|
||||
const debug_allocator = @import("build_options").debug_allocator;
|
||||
const disable_terminal_mouse = @import("build_options").disable_terminal_mouse;
|
||||
const json = @import("json.zig");
|
||||
|
||||
fn readFromSignalFd(signal_fd: std.os.fd_t) !void {
|
||||
var buf: [@sizeOf(os.linux.signalfd_siginfo)]u8 align(8) = undefined;
|
||||
|
@ -14,31 +15,6 @@ fn readFromSignalFd(signal_fd: std.os.fd_t) !void {
|
|||
return error.Shutdown;
|
||||
}
|
||||
|
||||
pub fn utf8ValidateSlice(s: []const u8) bool {
|
||||
var i: usize = 0;
|
||||
while (i < s.len) {
|
||||
if (std.unicode.utf8ByteSequenceLength(s[i])) |cp_len| {
|
||||
if (i + cp_len > s.len) {
|
||||
log.err(.uni, "oh nos: {} {} {}\n", .{i + cp_len, i, s.len});
|
||||
log.err(.uni, "oh no: {}\n", .{s[i .. i + cp_len]});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std.unicode.utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {
|
||||
log.err(.uni, "oh no: {}\n", .{s[i .. i + cp_len]});
|
||||
return false;
|
||||
}
|
||||
i += cp_len;
|
||||
} else |err| {
|
||||
log.err(.uni, "oh noz: {} {} {} {} \"{}\" \n", .{s[i], s.len, i, err, s[0..i+1]});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
fn sigemptyset(set: *std.os.sigset_t) void {
|
||||
for (set) |*val| {
|
||||
val.* = 0;
|
||||
|
@ -103,7 +79,7 @@ pub const Bar = struct {
|
|||
// Serialize all bar items and put on stdout.
|
||||
try self.out_file.writer().writeAll("[");
|
||||
for (self.infos.items) |info, i| {
|
||||
try std.json.stringify(info, .{}, self.out_file.writer());
|
||||
try json.stringify(info, .{}, self.out_file.writer());
|
||||
|
||||
if (i < self.infos.items.len - 1) {
|
||||
try self.out_file.writer().writeAll(",");
|
||||
|
@ -239,9 +215,9 @@ pub const Bar = struct {
|
|||
// instead of looping and getting it, maybe then it would make more sense?
|
||||
// Anyway this just strips off the prefix of ',' so I can parse the json.
|
||||
if (line[0] == ',') line = line[1..line.len];
|
||||
const parseOptions = std.json.ParseOptions{ .allocator = self.allocator };
|
||||
const data = try std.json.parse(MouseEvent, &std.json.TokenStream.init(line), parseOptions);
|
||||
defer std.json.parseFree(MouseEvent, data, parseOptions);
|
||||
const parseOptions = json.ParseOptions{ .allocator = self.allocator };
|
||||
const data = try json.parse(MouseEvent, &json.TokenStream.init(line), parseOptions);
|
||||
defer json.parseFree(MouseEvent, data, parseOptions);
|
||||
|
||||
self.dispatch_click_event(data.name, data) catch {};
|
||||
// If mouse_event needs to store the event for after the call is finished,
|
||||
|
@ -316,9 +292,6 @@ pub const Bar = struct {
|
|||
}
|
||||
// If we reach here then it changed.
|
||||
try self.free_info(infoItem);
|
||||
if (!utf8ValidateSlice(info.full_text)) {
|
||||
std.log.err(.barerr, "Oh No: {}\n", .{info.full_text});
|
||||
}
|
||||
self.infos.items[index] = try self.dupe_info(info);
|
||||
try self.print_infos(false);
|
||||
}
|
||||
|
@ -332,7 +305,7 @@ pub fn initBar(allocator: *std.mem.Allocator) Bar {
|
|||
.widgets = undefined,
|
||||
.running = false,
|
||||
.infos = std.ArrayList(Info).init(allocator),
|
||||
.items_mutex = std.Mutex.init(),
|
||||
.items_mutex = std.Mutex{},
|
||||
.out_file = std.io.getStdOut(),
|
||||
};
|
||||
}
|
||||
|
|
1810
src/bar/json.zig
Normal file
1810
src/bar/json.zig
Normal file
File diff suppressed because it is too large
Load diff
250
src/bar/json/write_stream.zig
Normal file
250
src/bar/json/write_stream.zig
Normal file
|
@ -0,0 +1,250 @@
|
|||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const maxInt = std.math.maxInt;
|
||||
const State = enum {
|
||||
Complete,
|
||||
Value,
|
||||
ArrayStart,
|
||||
Array,
|
||||
ObjectStart,
|
||||
Object,
|
||||
};
|
||||
pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const Stream = OutStream;
|
||||
whitespace: std.json.StringifyOptions.Whitespace = std.json.StringifyOptions.Whitespace{
|
||||
.indent_level = 0,
|
||||
.indent = .{ .Space = 1 },
|
||||
},
|
||||
stream: OutStream,
|
||||
state_index: usize,
|
||||
state: [max_depth]State,
|
||||
pub fn init(stream: OutStream) Self {
|
||||
var self = Self{
|
||||
.stream = stream,
|
||||
.state_index = 1,
|
||||
.state = undefined,
|
||||
};
|
||||
self.state[0] = .Complete;
|
||||
self.state[1] = .Value;
|
||||
return self;
|
||||
}
|
||||
pub fn beginArray(self: *Self) !void {
|
||||
try self.stream.writeByte('[');
|
||||
self.state[self.state_index] = State.ArrayStart;
|
||||
self.whitespace.indent_level += 1;
|
||||
}
|
||||
pub fn beginObject(self: *Self) !void {
|
||||
try self.stream.writeByte('{');
|
||||
self.state[self.state_index] = State.ObjectStart;
|
||||
self.whitespace.indent_level += 1;
|
||||
}
|
||||
pub fn arrayElem(self: *Self) !void {
|
||||
const state = self.state[self.state_index];
|
||||
switch (state) {
|
||||
.Complete => unreachable,
|
||||
.Value => unreachable,
|
||||
.ObjectStart => unreachable,
|
||||
.Object => unreachable,
|
||||
.Array, .ArrayStart => {
|
||||
if (state == .Array) {
|
||||
try self.stream.writeByte(',');
|
||||
}
|
||||
self.state[self.state_index] = .Array;
|
||||
self.pushState(.Value);
|
||||
try self.indent();
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn objectField(self: *Self, name: []const u8) !void {
|
||||
const state = self.state[self.state_index];
|
||||
switch (state) {
|
||||
.Complete => unreachable,
|
||||
.Value => unreachable,
|
||||
.ArrayStart => unreachable,
|
||||
.Array => unreachable,
|
||||
.Object, .ObjectStart => {
|
||||
if (state == .Object) {
|
||||
try self.stream.writeByte(',');
|
||||
}
|
||||
self.state[self.state_index] = .Object;
|
||||
self.pushState(.Value);
|
||||
try self.indent();
|
||||
try self.writeEscapedString(name);
|
||||
try self.stream.writeByte(':');
|
||||
if (self.whitespace.separator) {
|
||||
try self.stream.writeByte(' ');
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn endArray(self: *Self) !void {
|
||||
switch (self.state[self.state_index]) {
|
||||
.Complete => unreachable,
|
||||
.Value => unreachable,
|
||||
.ObjectStart => unreachable,
|
||||
.Object => unreachable,
|
||||
.ArrayStart => {
|
||||
self.whitespace.indent_level -= 1;
|
||||
try self.stream.writeByte(']');
|
||||
self.popState();
|
||||
},
|
||||
.Array => {
|
||||
self.whitespace.indent_level -= 1;
|
||||
try self.indent();
|
||||
self.popState();
|
||||
try self.stream.writeByte(']');
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn endObject(self: *Self) !void {
|
||||
switch (self.state[self.state_index]) {
|
||||
.Complete => unreachable,
|
||||
.Value => unreachable,
|
||||
.ArrayStart => unreachable,
|
||||
.Array => unreachable,
|
||||
.ObjectStart => {
|
||||
self.whitespace.indent_level -= 1;
|
||||
try self.stream.writeByte('}');
|
||||
self.popState();
|
||||
},
|
||||
.Object => {
|
||||
self.whitespace.indent_level -= 1;
|
||||
try self.indent();
|
||||
self.popState();
|
||||
try self.stream.writeByte('}');
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn emitNull(self: *Self) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
try self.stringify(null);
|
||||
self.popState();
|
||||
}
|
||||
pub fn emitBool(self: *Self, value: bool) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
try self.stringify(value);
|
||||
self.popState();
|
||||
}
|
||||
pub fn emitNumber(
|
||||
self: *Self,
|
||||
value: anytype,
|
||||
) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
switch (@typeInfo(@TypeOf(value))) {
|
||||
.Int => |info| {
|
||||
if (info.bits < 53) {
|
||||
try self.stream.print("{}", .{value});
|
||||
self.popState();
|
||||
return;
|
||||
}
|
||||
if (value < 4503599627370496 and (!info.is_signed or value > -4503599627370496)) {
|
||||
try self.stream.print("{}", .{value});
|
||||
self.popState();
|
||||
return;
|
||||
}
|
||||
},
|
||||
.ComptimeInt => {
|
||||
return self.emitNumber(@as(std.math.IntFittingRange(value, value), value));
|
||||
},
|
||||
.Float, .ComptimeFloat => if (@floatCast(f64, value) == value) {
|
||||
try self.stream.print("{}", .{@floatCast(f64, value)});
|
||||
self.popState();
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
try self.stream.print("\"{}\"", .{value});
|
||||
self.popState();
|
||||
}
|
||||
pub fn emitString(self: *Self, string: []const u8) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
try self.writeEscapedString(string);
|
||||
self.popState();
|
||||
}
|
||||
fn writeEscapedString(self: *Self, string: []const u8) !void {
|
||||
assert(std.unicode.utf8ValidateSlice(string));
|
||||
try self.stringify(string);
|
||||
}
|
||||
pub fn emitJson(self: *Self, json: std.json.Value) Stream.Error!void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
try self.stringify(json);
|
||||
self.popState();
|
||||
}
|
||||
fn indent(self: *Self) !void {
|
||||
assert(self.state_index >= 1);
|
||||
try self.stream.writeByte('\n');
|
||||
try self.whitespace.outputIndent(self.stream);
|
||||
}
|
||||
fn pushState(self: *Self, state: State) void {
|
||||
self.state_index += 1;
|
||||
self.state[self.state_index] = state;
|
||||
}
|
||||
fn popState(self: *Self) void {
|
||||
self.state_index -= 1;
|
||||
}
|
||||
fn stringify(self: *Self, value: anytype) !void {
|
||||
try std.json.stringify(value, std.json.StringifyOptions{
|
||||
.whitespace = self.whitespace,
|
||||
}, self.stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
pub fn writeStream(
|
||||
out_stream: anytype,
|
||||
comptime max_depth: usize,
|
||||
) WriteStream(@TypeOf(out_stream), max_depth) {
|
||||
return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
|
||||
}
|
||||
test "json write stream" {
|
||||
var out_buf: [1024]u8 = undefined;
|
||||
var slice_stream = std.io.fixedBufferStream(&out_buf);
|
||||
const out = slice_stream.outStream();
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
var w = std.json.writeStream(out, 10);
|
||||
try w.beginObject();
|
||||
try w.objectField("object");
|
||||
try w.emitJson(try getJsonObject(&arena_allocator.allocator));
|
||||
try w.objectField("string");
|
||||
try w.emitString("This is a string");
|
||||
try w.objectField("array");
|
||||
try w.beginArray();
|
||||
try w.arrayElem();
|
||||
try w.emitString("Another string");
|
||||
try w.arrayElem();
|
||||
try w.emitNumber(@as(i32, 1));
|
||||
try w.arrayElem();
|
||||
try w.emitNumber(@as(f32, 3.5));
|
||||
try w.endArray();
|
||||
try w.objectField("int");
|
||||
try w.emitNumber(@as(i32, 10));
|
||||
try w.objectField("float");
|
||||
try w.emitNumber(@as(f32, 3.5));
|
||||
try w.endObject();
|
||||
const result = slice_stream.getWritten();
|
||||
const expected =
|
||||
\\{
|
||||
\\ "object": {
|
||||
\\ "one": 1,
|
||||
\\ "two": 2.0e+00
|
||||
\\ },
|
||||
\\ "string": "This is a string",
|
||||
\\ "array": [
|
||||
\\ "Another string",
|
||||
\\ 1,
|
||||
\\ 3.5e+00
|
||||
\\ ],
|
||||
\\ "int": 10,
|
||||
\\ "float": 3.5e+00
|
||||
\\}
|
||||
;
|
||||
std.testing.expect(std.mem.eql(u8, expected, result));
|
||||
}
|
||||
fn getJsonObject(allocator: *std.mem.Allocator) !std.json.Value {
|
||||
var value = std.json.Value{ .Object = std.json.ObjectMap.init(allocator) };
|
||||
_ = try value.Object.put("one", std.json.Value{ .Integer = @intCast(i64, 1) });
|
||||
_ = try value.Object.put("two", std.json.Value{ .Float = 2.0 });
|
||||
return value;
|
||||
}
|
|
@ -18,7 +18,7 @@ const Info = @import("types/info.zig");
|
|||
const debug_allocator = @import("build_options").debug_allocator;
|
||||
|
||||
// Set the log level to warning
|
||||
//pub const log_level: std.log.Level = .warn;
|
||||
pub const log_level: std.log.Level = .warn;
|
||||
// Define root.log to override the std implementation
|
||||
pub fn log(
|
||||
comptime level: std.log.Level,
|
||||
|
@ -63,7 +63,6 @@ pub fn main() !void {
|
|||
if (!debug_allocator) arena.deinit();
|
||||
}
|
||||
|
||||
|
||||
var bar = barImpl.initBar(allocator);
|
||||
var br = Bar.init(&bar);
|
||||
|
||||
|
@ -78,8 +77,8 @@ pub fn main() !void {
|
|||
bar.widgets = widgets[0..];
|
||||
try br.start();
|
||||
if (debug_allocator) {
|
||||
std.log.debug(.main, "Finished cleanup, last allocation info.\n", .{});
|
||||
std.log.debug(.main, "\n{}\n", .{dbgAlloc.info});
|
||||
std.debug.print("Finished cleanup, last allocation info.\n", .{});
|
||||
std.debug.print("\n{}\n", .{dbgAlloc.info});
|
||||
dbgAlloc.printRemainingStackTraces();
|
||||
dbgAlloc.deinit();
|
||||
}
|
||||
|
|
|
@ -129,6 +129,19 @@ pub const MemoryWidget = struct {
|
|||
self.update_bar() catch {};
|
||||
}
|
||||
|
||||
pub fn clear_cache(self: *MemoryWidget) !void {
|
||||
var buffer: [512 * 512]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
var allocator = &fba.allocator;
|
||||
var proc = try std.ChildProcess.init(&[_][]const u8{ "bash", "/home/kitteh/Scripts/drop-cache.sh" }, allocator);
|
||||
proc.stdout_behavior = .Close;
|
||||
proc.stdin_behavior = .Close;
|
||||
proc.stderr_behavior = .Close;
|
||||
try proc.spawn();
|
||||
_ = try proc.kill();
|
||||
proc.deinit();
|
||||
}
|
||||
|
||||
fn update_bar(self: *MemoryWidget) !void {
|
||||
var buffer: [512]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
|
@ -191,6 +204,11 @@ pub const MemoryWidget = struct {
|
|||
.full_text = text,
|
||||
.markup = "pango",
|
||||
});
|
||||
if (kibibytesToMegabytes(memInfo.cached) > 1000) {
|
||||
self.clear_cache() catch |err| {
|
||||
std.log.err(.memory, "Can't clear cache {}.\n", .{err});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(self: *MemoryWidget) anyerror!void {
|
||||
|
|
|
@ -64,7 +64,7 @@ pub const NetworkWidget = struct {
|
|||
network_infos: std.ArrayList(NetworkInfo),
|
||||
num_interfaces: u8 = 0,
|
||||
current_interface: u8 = 0,
|
||||
update_mutex: std.Mutex = std.Mutex.init(),
|
||||
update_mutex: std.Mutex = std.Mutex{},
|
||||
|
||||
pub fn name(self: *NetworkWidget) []const u8 {
|
||||
return "network";
|
||||
|
@ -94,12 +94,16 @@ pub const NetworkWidget = struct {
|
|||
pub fn update_network_infos(self: *NetworkWidget) anyerror!void {
|
||||
const lock = self.update_mutex.acquire();
|
||||
defer lock.release();
|
||||
std.log.debug(.network, "Updating network info.\n", .{});
|
||||
for (self.network_infos.items) |info| {
|
||||
freeString(self.allocator, info.network_info);
|
||||
}
|
||||
self.num_interfaces = 0;
|
||||
var proc = try std.ChildProcess.init(&[_][]const u8{ "nmcli", "-f", "common", "-c", "no", "d" }, self.allocator);
|
||||
defer { _ = proc.kill() catch {}; proc.deinit(); }
|
||||
defer {
|
||||
_ = proc.kill() catch {};
|
||||
proc.deinit();
|
||||
}
|
||||
proc.stdout_behavior = .Pipe;
|
||||
try proc.spawn();
|
||||
var i: u8 = 0;
|
||||
|
|
Loading…
Reference in a new issue