This is a SOAP request to fetch currency exchange rates by calling wsdl service.
The break of while loop is not elegant as it currently relies on knowing the last currency, but I could not find better solution by handling the error in case of no match. Recommendations and improvements are welcome! Thanks.
const std = @import("std");
const FXRate = struct {
unit: []u8,
currency: []u8,
value: []u8,
};
var pos_end: usize = 0;
fn fetchRates(allocator: std.mem.Allocator) ![]u8 {
const request_text: []const u8 =
\\ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.mnb.hu/webservices/">
\\ <soapenv:Header/>
\\ <soapenv:Body>
\\ <web:GetCurrentExchangeRates/>
\\ </soapenv:Body>
\\ </soapenv:Envelope>
;
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("http://www.mnb.hu/arfolyamok.asmx?wsdl");
var headers = std.http.Headers{ .allocator = allocator };
defer headers.deinit();
try headers.append("content-type", "application/xml");
var request = try client.request(.POST, uri, headers, .{});
defer request.deinit();
request.transfer_encoding = .chunked;
try request.start();
try request.writer().writeAll(request_text);
try request.finish();
try request.wait();
return try request.reader().readAllAlloc(allocator, 4096);
}
fn getTag(allocator: std.mem.Allocator, message: []u8, tag_name: []const u8) ![]u8 {
var pos_start = std.mem.indexOfPos(u8, message, 0, tag_name) orelse unreachable;
pos_start = pos_start + tag_name.len + 2; // 2 = length of ="
pos_end = pos_start + 1;
while (message[pos_end] != '\"') {
pos_end += 1;
}
return allocator.dupe(u8, message[pos_start..pos_end]);
}
fn getTagValue(allocator: std.mem.Allocator, message: []u8) ![]u8 {
var pos_start = std.mem.indexOfPos(u8, message, 0, ">") orelse unreachable;
pos_end = std.mem.indexOfPos(u8, message, 0, "<") orelse unreachable;
// 4 = length of >
return allocator.dupe(u8, message[pos_start + 4 .. pos_end]);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const message = try fetchRates(allocator);
defer allocator.free(message);
var fx_rates = std.ArrayList(FXRate).init(allocator);
defer fx_rates.deinit();
var tag_name: []const u8 = "Day date";
std.debug.print("Date: {s}\n", .{try getTag(allocator, message, tag_name)});
std.debug.print("Rate unit\tCurrency\tValue\n", .{});
var pos_start = pos_end;
while (true) {
const unit = try getTag(allocator, message[pos_start..], "Rate unit");
pos_start += pos_end;
const currency = try getTag(allocator, message[pos_start..], "curr");
pos_start += pos_end;
const value = try getTagValue(allocator, message[pos_start..]);
pos_start += pos_end;
const r: FXRate = .{ .unit = unit, .currency = currency, .value = value };
try fx_rates.append(r);
if (std.mem.eql(u8, currency, "ZAR") == true) break;
}
for (fx_rates.items) |rate| {
std.debug.print("{s}\t\t{s}\t\t{s}\n", .{ rate.unit, rate.currency, rate.value });
}
}