-
Notifications
You must be signed in to change notification settings - Fork 6
SHA1 digest (NW.js)
In this example we're going to create an app that calculates SHA-1 digests of files, using a built-in function of Zig's standard library.
First, we'll create the basic skeleton:
mkdir sha1
cd sha1
npm init -y
npm install node-zigar
mkdir src zigCreate sha1.zig:
const std = @import("std");
pub fn sha1(bytes: []const u8) [std.crypto.hash.Sha1.digest_length * 2]u8 {
var digest: [std.crypto.hash.Sha1.digest_length]u8 = undefined;
std.crypto.hash.Sha1.hash(bytes, &digest, .{});
return std.fmt.bytesToHex(digest, .lower);
}Then index.js:
require('node-zigar/cjs');
const { sha1 } = require('./zig/sha1.zig');
const isMac = process.platform === 'darwin'
nw.Window.open('./src/index.html', { width: 800, height: 600 }, (browser) => {
// create menu bar
const onOpenClick = () => {
const { window: { document } } = browser;
document.getElementById('file').click();
};
const onCloseClick = () => {
browser.close();
};
const menuBar = new nw.Menu({ type: 'menubar' });
const fileMenu = new nw.Menu();
fileMenu.append(new nw.MenuItem({ label: 'Open', click: onOpenClick }));
fileMenu.append(new nw.MenuItem({ type: 'separator' }));
fileMenu.append(new nw.MenuItem({ label: (isMac) ? 'Close' : 'Quit', click: onCloseClick }));
menuBar.append(new nw.MenuItem({ label: 'File', submenu: fileMenu }));
browser.menu = menuBar;
browser.window.onload = (evt) => {
// find page elements and attach handler
const { target: document } = evt;
const file = document.getElementById('file');
const heading = document.getElementById('heading');
file.onchange = async (evt) => {
const { target: { files: [ file ] } } = evt;
if (file) {
const data = await file.arrayBuffer();
const hash = sha1(data).string;
heading.textContent = hash;
}
};
};
});Then index.html:
<!DOCTYPE html>
<html>
<head>
<title>SHA-1 Digest</title>
</head>
<body>
<h2 id="heading">Use file menu to open a file</h2>
<input id="file" type="file" style="visibility: hidden;">
</body>
</html>Adjust main in package.json:
"main": "src/index.js",Now we're ready to test the app:
[NW.js directory]/nw .In the example, sha1() returns an array of u8. On the JavaScript side it's represented by a
object. To get a string, you need to access its string property.
Zigar lets you to flag certain functions as returning strings. To do so, you declare a struct
type with a particular name at the root level:
const module_ns = @This();
pub const @"meta(zigar)" = struct {
pub fn isDeclString(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
return switch (T) {
module_ns => switch (name) {
.sha1 => true,
else => false,
},
else => false,
};
}
};During export, isDeclString() is invoked when a function's return value is something that can be
interpreted as a text string (e.g. []const u8). With the above declaration in place, we can
simplify our JavaScript:
const hash = sha1(data);You can use the following to threat all occurences of u8 and u16 as text:
pub const @"meta(zigar)" = struct {
pub fn isDeclString(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
return true;
}
pub fn isFieldString(comptime T: type, comptime _: std.meta.FieldEnum(T)) bool {
return true;
}
};We're going to follow the same steps as described in the hello world tutorial. First, we'll alter the require statement so it references a node-zigar module instead of a Zig file:
const { sha1 } = require('../lib/sha1.zigar');Then we add node-zigar.config.json to the app's root directory:
{
"optimize": "ReleaseSmall",
"modules": {
"lib/sha1.zigar": {
"source": "zig/sha1.zig"
}
},
"targets": [
{ "platform": "win32", "arch": "x64" },
{ "platform": "win32", "arch": "arm64" },
{ "platform": "win32", "arch": "ia32" },
{ "platform": "linux", "arch": "x64" },
{ "platform": "linux", "arch": "arm64" },
{ "platform": "darwin", "arch": "x64" },
{ "platform": "darwin", "arch": "arm64" }
]
}We build the library files:
npx node-zigar buildAt this point the app is ready to be packaged.
You can find the complete source code for this example here.
Finally, we have an app that does something that you can see! You learned how to create menus, open files, and work on their contents. This example is still relatively simple. All we're doing is calling a function. It accepts an uncomplicated argument and returns an uncomplicated value. In the next example, the function involved will take more complicated arguments and return something complicated as well.