Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 7 additions & 28 deletions src/generator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,25 @@ fn generateCodeFromJsonContents(allocator: std.mem.Allocator, io: std.Io, json_c
var swagger = try models.SwaggerDocument.parseFromJson(allocator, json_contents);
defer swagger.deinit(allocator);
std.log.info("Successfully parsed Swagger v2.0 document", .{});
try generateCodeFromSwaggerDocument(allocator, io, swagger, args);
try generateCodeFromDocument(allocator, io, swagger, args, SwaggerConverter);
},
.v3_0 => {
var openapi = try models.OpenApiDocument.parseFromJson(allocator, json_contents);
defer openapi.deinit(allocator);
std.log.info("Successfully parsed OpenAPI v3.0 document", .{});
try generateCodeFromOpenApiDocument(allocator, io, openapi, args);
try generateCodeFromDocument(allocator, io, openapi, args, OpenApiConverter);
},
.v3_1 => {
var openapi31 = try models.OpenApi31Document.parseFromJson(allocator, json_contents);
defer openapi31.deinit(allocator);
std.log.info("Successfully parsed OpenAPI v3.1 document", .{});
try generateCodeFromOpenApi31Document(allocator, io, openapi31, args);
try generateCodeFromDocument(allocator, io, openapi31, args, OpenApi31Converter);
},
.v3_2 => {
var openapi32 = try models.OpenApi32Document.parseFromJson(allocator, json_contents);
defer openapi32.deinit(allocator);
std.log.info("Successfully parsed OpenAPI v3.2 document", .{});
try generateCodeFromOpenApi32Document(allocator, io, openapi32, args);
try generateCodeFromDocument(allocator, io, openapi32, args, OpenApi32Converter);
},
else => {
return GeneratorErrors.UnsupportedOpenAPIVersion;
Expand Down Expand Up @@ -134,30 +134,9 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni
std.log.info("Code generated successfully and written to '{s}'.", .{output_path});
}

fn generateCodeFromSwaggerDocument(allocator: std.mem.Allocator, io: std.Io, swagger: models.SwaggerDocument, args: cli.CliArgs) !void {
var swagger_converter = SwaggerConverter.init(allocator);
var unified_doc = try swagger_converter.convert(swagger);
defer unified_doc.deinit(allocator);
try generateCodeFromUnifiedDocument(allocator, io, unified_doc, args);
}

fn generateCodeFromOpenApiDocument(allocator: std.mem.Allocator, io: std.Io, openapi: models.OpenApiDocument, args: cli.CliArgs) !void {
var openapi_converter = OpenApiConverter.init(allocator);
var unified_doc = try openapi_converter.convert(openapi);
defer unified_doc.deinit(allocator);
try generateCodeFromUnifiedDocument(allocator, io, unified_doc, args);
}

fn generateCodeFromOpenApi31Document(allocator: std.mem.Allocator, io: std.Io, openapi: models.OpenApi31Document, args: cli.CliArgs) !void {
var openapi31_converter = OpenApi31Converter.init(allocator);
var unified_doc = try openapi31_converter.convert(openapi);
defer unified_doc.deinit(allocator);
try generateCodeFromUnifiedDocument(allocator, io, unified_doc, args);
}

fn generateCodeFromOpenApi32Document(allocator: std.mem.Allocator, io: std.Io, openapi: models.OpenApi32Document, args: cli.CliArgs) !void {
var openapi32_converter = OpenApi32Converter.init(allocator);
var unified_doc = try openapi32_converter.convert(openapi);
fn generateCodeFromDocument(allocator: std.mem.Allocator, io: std.Io, doc: anytype, args: cli.CliArgs, comptime Converter: type) !void {
var converter = Converter.init(allocator);
var unified_doc = try converter.convert(doc);
defer unified_doc.deinit(allocator);
try generateCodeFromUnifiedDocument(allocator, io, unified_doc, args);
}
Expand Down
60 changes: 5 additions & 55 deletions src/generators/unified/api_generator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ const Schema = @import("../../models/common/document.zig").Schema;
const SchemaType = @import("../../models/common/document.zig").SchemaType;
const Parameter = @import("../../models/common/document.zig").Parameter;
const media_type = @import("../../media_type.zig");

fn isIdentStart(c: u8) bool {
return std.ascii.isAlphabetic(c) or c == '_';
}
const ident = @import("ident_utils.zig");

const BodyKind = enum { none, json, binary, text, form };

Expand Down Expand Up @@ -55,35 +52,6 @@ fn bodyKindFor(operation: Operation) BodyKind {
return classifyBody(param.content_type);
}

fn isIdentContinue(c: u8) bool {
return std.ascii.isAlphanumeric(c) or c == '_';
}

fn isReservedIdent(name: []const u8) bool {
const reserved = [_][]const u8{
"addrspace", "align", "allowzero", "and", "anyerror", "anyframe", "anyopaque", "anytype",
"asm", "async", "await", "bool", "break", "callconv", "catch", "comptime",
"const", "continue", "defer", "else", "enum", "errdefer", "error", "export",
"extern", "false", "fn", "for", "if", "inline", "isize", "linksection",
"noalias", "noreturn", "nosuspend", "null", "opaque", "or", "orelse", "packed",
"pub", "resume", "return", "struct", "suspend", "switch", "test", "threadlocal",
"true", "try", "type", "undefined", "union", "unreachable", "usize", "usingnamespace",
"var", "void", "volatile", "while",
};
for (reserved) |word| {
if (std.mem.eql(u8, name, word)) return true;
}
return false;
}

fn isBareIdentifier(name: []const u8) bool {
if (name.len == 0 or !isIdentStart(name[0]) or isReservedIdent(name)) return false;
for (name[1..]) |c| {
if (!isIdentContinue(c)) return false;
}
return true;
}

const OperationRef = struct {
path: []const u8,
method: []const u8,
Expand Down Expand Up @@ -178,25 +146,7 @@ pub const UnifiedApiGenerator = struct {
}

fn appendIdentifier(self: *UnifiedApiGenerator, name: []const u8) !void {
if (isBareIdentifier(name)) {
try self.buffer.appendSlice(self.allocator, name);
return;
}

try self.buffer.appendSlice(self.allocator, "@\"");
for (name) |c| {
switch (c) {
'\\', '"' => {
try self.buffer.append(self.allocator, '\\');
try self.buffer.append(self.allocator, c);
},
'\n' => try self.buffer.appendSlice(self.allocator, "\\n"),
'\r' => try self.buffer.appendSlice(self.allocator, "\\r"),
'\t' => try self.buffer.appendSlice(self.allocator, "\\t"),
else => try self.buffer.append(self.allocator, c),
}
}
try self.buffer.appendSlice(self.allocator, "\"");
try ident.appendIdentifier(&self.buffer, self.allocator, name);
}

fn appendLineComment(self: *UnifiedApiGenerator, text: []const u8) !void {
Expand Down Expand Up @@ -1331,11 +1281,11 @@ pub const UnifiedApiGenerator = struct {
errdefer out.deinit(self.allocator);
for (value, 0..) |c, i| {
const lower = std.ascii.toLower(c);
const valid = if (i == 0) isIdentStart(lower) else isIdentContinue(lower);
const valid = if (i == 0) ident.isIdentStart(lower) else ident.isIdentContinue(lower);
try out.append(self.allocator, if (valid) lower else '_');
}
if (out.items.len == 0 or !isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
if (isReservedIdent(out.items)) try out.appendSlice(self.allocator, "_");
if (out.items.len == 0 or !ident.isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
if (ident.isReservedIdent(out.items)) try out.appendSlice(self.allocator, "_");
return try out.toOwnedSlice(self.allocator);
}

Expand Down
100 changes: 100 additions & 0 deletions src/generators/unified/ident_utils.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const std = @import("std");

pub fn isIdentStart(c: u8) bool {
return std.ascii.isAlphabetic(c) or c == '_';
}

pub fn isIdentContinue(c: u8) bool {
return std.ascii.isAlphanumeric(c) or c == '_';
}

pub fn isReservedIdent(name: []const u8) bool {
const reserved = [_][]const u8{
"addrspace", "align", "allowzero", "and", "anyerror", "anyframe", "anyopaque", "anytype",
"asm", "async", "await", "bool", "break", "callconv", "catch", "comptime",
"const", "continue", "defer", "else", "enum", "errdefer", "error", "export",
"extern", "false", "fn", "for", "if", "inline", "isize", "linksection",
"noalias", "noreturn", "nosuspend", "null", "opaque", "or", "orelse", "packed",
"pub", "resume", "return", "struct", "suspend", "switch", "test", "threadlocal",
"true", "try", "type", "undefined", "union", "unreachable", "usize", "usingnamespace",
"var", "void", "volatile", "while",
};
for (reserved) |word| {
if (std.mem.eql(u8, name, word)) return true;
}
return false;
}

pub fn isBareIdentifier(name: []const u8) bool {
if (name.len == 0 or !isIdentStart(name[0]) or isReservedIdent(name)) return false;
for (name[1..]) |c| {
if (!isIdentContinue(c)) return false;
}
return true;
}

pub fn appendIdentifier(buffer: *std.ArrayList(u8), allocator: std.mem.Allocator, name: []const u8) !void {
if (isBareIdentifier(name)) {
try buffer.appendSlice(allocator, name);
return;
}
try buffer.appendSlice(allocator, "@\"");
for (name) |c| {
switch (c) {
'\\', '"' => {
try buffer.append(allocator, '\\');
try buffer.append(allocator, c);
},
'\n' => try buffer.appendSlice(allocator, "\\n"),
'\r' => try buffer.appendSlice(allocator, "\\r"),
'\t' => try buffer.appendSlice(allocator, "\\t"),
else => try buffer.append(allocator, c),
}
}
try buffer.appendSlice(allocator, "\"");
}

test "isIdentStart" {
try std.testing.expect(isIdentStart('a'));
try std.testing.expect(isIdentStart('Z'));
try std.testing.expect(isIdentStart('_'));
try std.testing.expect(!isIdentStart('0'));
try std.testing.expect(!isIdentStart('-'));
}

test "isIdentContinue" {
try std.testing.expect(isIdentContinue('a'));
try std.testing.expect(isIdentContinue('Z'));
try std.testing.expect(isIdentContinue('_'));
try std.testing.expect(isIdentContinue('0'));
try std.testing.expect(!isIdentContinue('-'));
}

test "isReservedIdent" {
try std.testing.expect(isReservedIdent("if"));
try std.testing.expect(isReservedIdent("return"));
try std.testing.expect(isReservedIdent("struct"));
try std.testing.expect(!isReservedIdent("foo"));
try std.testing.expect(!isReservedIdent(""));
}

test "isBareIdentifier" {
try std.testing.expect(isBareIdentifier("foo"));
try std.testing.expect(isBareIdentifier("_bar"));
try std.testing.expect(!isBareIdentifier(""));
try std.testing.expect(!isBareIdentifier("0foo"));
try std.testing.expect(!isBareIdentifier("if"));
}

test "appendIdentifier" {
var buf = std.ArrayList(u8).empty;
defer buf.deinit(std.testing.allocator);
try appendIdentifier(&buf, std.testing.allocator, "simple");
try std.testing.expectEqualStrings("simple", buf.items);
buf.clearRetainingCapacity();
try appendIdentifier(&buf, std.testing.allocator, "has space");
try std.testing.expectEqualStrings("@\"has space\"", buf.items);
buf.clearRetainingCapacity();
try appendIdentifier(&buf, std.testing.allocator, "quote\"here");
try std.testing.expectEqualStrings("@\"quote\\\"here\"", buf.items);
}
62 changes: 6 additions & 56 deletions src/generators/unified/model_generator.zig
Original file line number Diff line number Diff line change
@@ -1,39 +1,7 @@
const std = @import("std");
const UnifiedDocument = @import("../../models/common/document.zig").UnifiedDocument;
const Schema = @import("../../models/common/document.zig").Schema;

fn isIdentStart(c: u8) bool {
return std.ascii.isAlphabetic(c) or c == '_';
}

fn isIdentContinue(c: u8) bool {
return std.ascii.isAlphanumeric(c) or c == '_';
}

fn isReservedIdent(name: []const u8) bool {
const reserved = [_][]const u8{
"addrspace", "align", "allowzero", "and", "anyerror", "anyframe", "anyopaque", "anytype",
"asm", "async", "await", "bool", "break", "callconv", "catch", "comptime",
"const", "continue", "defer", "else", "enum", "errdefer", "error", "export",
"extern", "false", "fn", "for", "if", "inline", "isize", "linksection",
"noalias", "noreturn", "nosuspend", "null", "opaque", "or", "orelse", "packed",
"pub", "resume", "return", "struct", "suspend", "switch", "test", "threadlocal",
"true", "try", "type", "undefined", "union", "unreachable", "usize", "usingnamespace",
"var", "void", "volatile", "while",
};
for (reserved) |word| {
if (std.mem.eql(u8, name, word)) return true;
}
return false;
}

fn isBareIdentifier(name: []const u8) bool {
if (name.len == 0 or !isIdentStart(name[0]) or isReservedIdent(name)) return false;
for (name[1..]) |c| {
if (!isIdentContinue(c)) return false;
}
return true;
}
const ident = @import("ident_utils.zig");

fn isExtensibleRequest(name: []const u8) bool {
return std.mem.eql(u8, name, "CreateResponse") or
Expand Down Expand Up @@ -69,25 +37,7 @@ pub const UnifiedModelGenerator = struct {
}

fn appendIdentifier(self: *UnifiedModelGenerator, name: []const u8) !void {
if (isBareIdentifier(name)) {
try self.buffer.appendSlice(self.allocator, name);
return;
}

try self.buffer.appendSlice(self.allocator, "@\"");
for (name) |c| {
switch (c) {
'\\', '"' => {
try self.buffer.append(self.allocator, '\\');
try self.buffer.append(self.allocator, c);
},
'\n' => try self.buffer.appendSlice(self.allocator, "\\n"),
'\r' => try self.buffer.appendSlice(self.allocator, "\\r"),
'\t' => try self.buffer.appendSlice(self.allocator, "\\t"),
else => try self.buffer.append(self.allocator, c),
}
}
try self.buffer.appendSlice(self.allocator, "\"");
try ident.appendIdentifier(&self.buffer, self.allocator, name);
}

fn generateHeader(self: *UnifiedModelGenerator) !void {
Expand Down Expand Up @@ -177,15 +127,15 @@ pub const UnifiedModelGenerator = struct {
if (insert_word_break) try out.append(self.allocator, '_');

const lower = std.ascii.toLower(c);
const valid = if (out.items.len == 0) isIdentStart(lower) else isIdentContinue(lower);
const valid = if (out.items.len == 0) ident.isIdentStart(lower) else ident.isIdentContinue(lower);
const byte = if (valid) lower else '_';
if (byte == '_' and prev_was_underscore) continue;
try out.append(self.allocator, byte);
prev_was_underscore = byte == '_';
}
while (out.items.len > 0 and out.items[out.items.len - 1] == '_') _ = out.pop();
if (out.items.len == 0 or !isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
if (isReservedIdent(out.items)) try out.appendSlice(self.allocator, "_");
if (out.items.len == 0 or !ident.isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
if (ident.isReservedIdent(out.items)) try out.appendSlice(self.allocator, "_");
return try out.toOwnedSlice(self.allocator);
}

Expand Down Expand Up @@ -291,7 +241,7 @@ pub const UnifiedModelGenerator = struct {
errdefer out.deinit(self.allocator);
try self.appendTitleIdentPart(&out, owner_name);
try self.appendTitleIdentPart(&out, field_name);
if (out.items.len == 0 or !isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
if (out.items.len == 0 or !ident.isIdentStart(out.items[0])) try out.insert(self.allocator, 0, '_');
return try out.toOwnedSlice(self.allocator);
}

Expand Down
Loading
Loading