diff --git a/src/artifacts.zig b/src/artifacts.zig index 354635d..41b41f3 100644 --- a/src/artifacts.zig +++ b/src/artifacts.zig @@ -22,6 +22,8 @@ token: ?[]u8 = null, item_codes: std.ArrayList([]u8), +// ------------------------- API errors ------------------------ + pub const APIError = error { ServerUnavailable, RequestFailed, @@ -122,6 +124,8 @@ pub const EquipError = APIError || error { CharacterInCooldown, }; +// ------------------------- API result structs ------------------------ + pub const EquipmentSlot = enum { weapon, shield, @@ -624,6 +628,48 @@ pub const EquipResult = struct { } }; +pub const MapResult = struct { + pub const MapContent = struct { + type: []u8, + code: []u8, + }; + + name: []u8, + skin: []u8, + x: i64, + y: i64, + content: ?MapContent, + + pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap, allocator: Allocator) !MapResult { + _ = api; + + var content: ?MapContent = null; + if (json_utils.getObject(obj, "content")) |content_obj| { + content = MapContent{ + .type = (try json_utils.dupeString(allocator, content_obj, "type")) orelse return error.MissingProperty, + .code = (try json_utils.dupeString(allocator, content_obj, "code")) orelse return error.MissingProperty, + }; + } + + return MapResult{ + .name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty, + .skin = (try json_utils.dupeString(allocator, obj, "skin")) orelse return error.MissingProperty, + .x = json_utils.getInteger(obj, "x") orelse return error.MissingProperty, + .y = json_utils.getInteger(obj, "y") orelse return error.MissingProperty, + .content = content + }; + } + + pub fn deinit(self: MapResult, allocator: Allocator) void { + allocator.free(self.name); + allocator.free(self.skin); + if (self.content) |content| { + allocator.free(content.type); + allocator.free(content.code); + } + } +}; + pub const ArtifactsFetchResult = struct { arena: std.heap.ArenaAllocator, status: std.http.Status, @@ -634,6 +680,8 @@ pub const ArtifactsFetchResult = struct { } }; +// ------------------------- General API methods ------------------------ + pub fn init(allocator: Allocator) !ArtifactsAPI { const server = try allocator.dupe(u8, "https://api.artifactsmmo.com"); const server_uri = std.Uri.parse(server) catch unreachable; @@ -922,7 +970,6 @@ pub fn getItemCode(self: *const ArtifactsAPI, id: ItemId) ?[]const u8 { return self.item_codes.items[id]; } - // ------------------------- Endpoints ------------------------ pub fn getServerStatus(self: *ArtifactsAPI) !ServerStatus { @@ -935,7 +982,7 @@ pub fn getServerStatus(self: *ArtifactsAPI) !ServerStatus { ); } -pub fn getCharacter(self: *ArtifactsAPI, name: []const u8) !?Character { +pub fn getCharacter(self: *ArtifactsAPI, allocator: Allocator, name: []const u8) APIError!?Character { const path = try std.fmt.allocPrint(self.allocator, "/characters/{s}", .{name}); defer self.allocator.free(path); @@ -943,46 +990,20 @@ pub fn getCharacter(self: *ArtifactsAPI, name: []const u8) !?Character { APIError, null, Character, - Character.parse, .{ self.allocator }, + Character.parse, .{ allocator }, .{ .method = .GET, .path = path } ); } -pub fn listMyCharacters(self: *ArtifactsAPI) !CharacterList { - const path = try std.fmt.allocPrint(self.allocator, "/my/characters", .{}); - defer self.allocator.free(path); - - const result = try self.fetch(.{ .method = .GET, .path = path }); - defer result.deinit(); - - if (result.status != .ok) { - return APIError.RequestFailed; - } - if (result.body == null) { - return APIError.ParseFailed; - } - - const body = json_utils.asArray(result.body.?) orelse return APIError.ParseFailed; - - var characters = try std.ArrayList(Character).initCapacity(self.allocator, body.items.len); - errdefer { - for (characters.items) |*char| { - char.deinit(); - } - characters.deinit(); - } - - for (body.items) |character_json| { - const character_obj = json_utils.asObject(character_json) orelse return APIError.ParseFailed; - const char = Character.parse(character_obj, self.allocator, self) catch return APIError.ParseFailed; - - characters.appendAssumeCapacity(char); - } - - return CharacterList{ - .allocator = self.allocator, - .items = characters.items - }; +pub fn listMyCharacters(self: *ArtifactsAPI, allocator: Allocator) APIError!std.ArrayList(Character) { + return self.fetchArray( + allocator, + APIError, + null, + Character, + Character.parse, .{ allocator }, + .{ .method = .GET, .path = "/my/characters" } + ); } pub fn actionFight(self: *ArtifactsAPI, name: []const u8) FightError!FightResult { @@ -1187,9 +1208,9 @@ pub fn getBankGold(self: *ArtifactsAPI) APIError!u64 { return @intCast(quantity); } -pub fn getBankItems(self: *ArtifactsAPI) APIError!std.ArrayList(ItemQuantity) { +pub fn getBankItems(self: *ArtifactsAPI, allocator: Allocator) APIError!std.ArrayList(ItemQuantity) { return self.fetchArray( - self.allocator, + allocator, APIError, null, ItemQuantity, @@ -1198,6 +1219,30 @@ pub fn getBankItems(self: *ArtifactsAPI) APIError!std.ArrayList(ItemQuantity) { ); } +pub fn getMap(self: *ArtifactsAPI, allocator: Allocator, x: i64, y: i64) APIError!?MapResult { + const path = try std.fmt.allocPrint(self.allocator, "/maps/{}/{}", .{x, y}); + defer self.allocator.free(path); + + return self.fetchOptionalObject( + APIError, + null, + MapResult, + MapResult.parse, .{ allocator }, + .{ .method = .GET, .path = path } + ); +} + +pub fn getMaps(self: *ArtifactsAPI, allocator: Allocator) APIError!ObjectList(MapResult) { + return self.fetchArray( + allocator, + APIError, + null, + MapResult, + MapResult.parse, .{ allocator }, + .{ .method = .GET, .path = "/maps", .paginated = true } + ); +} + test "parse date time" { try std.testing.expectEqual(1723069394.105, parseDateTime("2024-08-07T22:23:14.105Z").?); } diff --git a/src/character.zig b/src/character.zig index af2f41e..1d46d47 100644 --- a/src/character.zig +++ b/src/character.zig @@ -196,7 +196,7 @@ equipment: Equipment, inventory_max_items: i64, inventory: Inventory, -pub fn parse(obj: json.ObjectMap, child_allocator: Allocator, api: *ArtifactsAPI) !Character { +pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap, child_allocator: Allocator) !Character { var arena = try child_allocator.create(std.heap.ArenaAllocator); errdefer child_allocator.destroy(arena);