chore: replace custom snake case with case package
This commit is contained in:
parent
3e06eeca66
commit
87fc872f7d
4 changed files with 14 additions and 161 deletions
|
@ -215,6 +215,7 @@ fn configure(compile: *std.Build.Module, modules: std.StringHashMap(*std.Build.M
|
|||
compile.addImport("smithy", modules.get("smithy").?);
|
||||
compile.addImport("date", modules.get("date").?);
|
||||
compile.addImport("json", modules.get("json").?);
|
||||
compile.addImport("case", modules.get("case").?);
|
||||
if (include_time) compile.addImport("zeit", modules.get("zeit").?);
|
||||
}
|
||||
|
||||
|
@ -229,6 +230,10 @@ fn getDependencyModules(b: *std.Build, args: anytype) !std.StringHashMap(*std.Bu
|
|||
const dep_zeit = b.dependency("zeit", args);
|
||||
const mod_zeit = dep_zeit.module("zeit");
|
||||
try result.putNoClobber("zeit", mod_zeit);
|
||||
|
||||
const dep_case = b.dependency("case", args);
|
||||
const mod_case = dep_case.module("case");
|
||||
try result.putNoClobber("case", mod_case);
|
||||
// End External dependencies
|
||||
|
||||
// Private modules/dependencies
|
||||
|
|
|
@ -31,5 +31,9 @@
|
|||
.json = .{
|
||||
.path = "lib/json",
|
||||
},
|
||||
.case = .{
|
||||
.url = "git+https://github.com/travisstaloch/case.git#610caade88ca54d2745f115114b08e73e2c6fe02",
|
||||
.hash = "N-V-__8AAIfIAAC_RzCtghVVBVdqUzB8AaaGIyvK2WWz38bC",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("std");
|
||||
const smithy = @import("smithy");
|
||||
const snake = @import("snake.zig");
|
||||
const Hasher = @import("Hasher.zig");
|
||||
const case = @import("case");
|
||||
|
||||
var verbose = false;
|
||||
|
||||
|
@ -479,9 +479,10 @@ fn constantName(allocator: std.mem.Allocator, id: []const u8) ![]const u8 {
|
|||
// snake turns this into dev_ops, which is a little weird
|
||||
if (std.mem.eql(u8, id, "DevOps Guru")) return try std.fmt.allocPrint(allocator, "devops_guru", .{});
|
||||
if (std.mem.eql(u8, id, "FSx")) return try std.fmt.allocPrint(allocator, "fsx", .{});
|
||||
if (std.mem.eql(u8, id, "ETag")) return try std.fmt.allocPrint(allocator, "e_tag", .{});
|
||||
|
||||
// Not a special case - just snake it
|
||||
return try snake.fromPascalCase(allocator, id);
|
||||
return try case.allocTo(allocator, .snake, id);
|
||||
}
|
||||
|
||||
const FileGenerationState = struct {
|
||||
|
@ -503,7 +504,7 @@ fn outputIndent(state: GenerationState, writer: anytype) !void {
|
|||
try writer.writeByteNTimes(' ', n_chars);
|
||||
}
|
||||
fn generateOperation(allocator: std.mem.Allocator, operation: smithy.ShapeInfo, file_state: FileGenerationState, writer: anytype) !void {
|
||||
const snake_case_name = try snake.fromPascalCase(allocator, operation.name);
|
||||
const snake_case_name = try constantName(allocator, operation.name);
|
||||
defer allocator.free(snake_case_name);
|
||||
|
||||
var type_stack = std.ArrayList(*const smithy.ShapeInfo).init(allocator);
|
||||
|
@ -822,7 +823,7 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
|
|||
var payload: ?[]const u8 = null;
|
||||
for (members) |member| {
|
||||
// This is our mapping
|
||||
const snake_case_member = try snake.fromPascalCase(state.allocator, member.name);
|
||||
const snake_case_member = try constantName(state.allocator, member.name);
|
||||
// So it looks like some services have duplicate names?! Check out "httpMethod"
|
||||
// in API Gateway. Not sure what we're supposed to do there. Checking the go
|
||||
// sdk, they move this particular duplicate to 'http_method' - not sure yet
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
const std = @import("std");
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
||||
pub fn fromPascalCase(allocator: std.mem.Allocator, name: []const u8) ![]u8 {
|
||||
const rc = try allocator.alloc(u8, name.len * 2); // This is overkill, but is > the maximum length possibly needed
|
||||
errdefer allocator.free(rc);
|
||||
var utf8_name = (std.unicode.Utf8View.init(name) catch unreachable).iterator();
|
||||
var target_inx: u64 = 0;
|
||||
var curr_char = (try isAscii(utf8_name.nextCodepoint())).?;
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
var prev_char = curr_char;
|
||||
if (try isAscii(utf8_name.nextCodepoint())) |ch| {
|
||||
curr_char = ch;
|
||||
} else {
|
||||
// Single character only - we're done here
|
||||
_ = setNext(0, rc, target_inx);
|
||||
return rc[0..target_inx];
|
||||
}
|
||||
while (try isAscii(utf8_name.nextCodepoint())) |next_char| {
|
||||
if (next_char == ' ') {
|
||||
// a space shouldn't be happening. But if it does, it clues us
|
||||
// in pretty well:
|
||||
//
|
||||
// MyStuff Is Awesome
|
||||
// |^
|
||||
// |next_char
|
||||
// ^
|
||||
// prev_codepoint/ascii_prev_char (and target_inx)
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
target_inx = setNext('_', rc, target_inx);
|
||||
var maybe_curr_char = (try isAscii(utf8_name.nextCodepoint()));
|
||||
if (maybe_curr_char == null) {
|
||||
std.log.err("Error on fromPascalCase processing name '{s}'", .{name});
|
||||
}
|
||||
curr_char = maybe_curr_char.?;
|
||||
maybe_curr_char = (try isAscii(utf8_name.nextCodepoint()));
|
||||
if (maybe_curr_char == null) {
|
||||
// We have reached the end of the string (e.g. "Resource Explorer 2")
|
||||
// We need to do this check before we setNext, so that we don't
|
||||
// end up duplicating the last character
|
||||
break;
|
||||
// std.log.err("Error on fromPascalCase processing name '{s}', curr_char = '{}'", .{ name, curr_char });
|
||||
}
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
prev_char = curr_char;
|
||||
curr_char = maybe_curr_char.?;
|
||||
continue;
|
||||
}
|
||||
if (between(curr_char, 'A', 'Z')) {
|
||||
if (isAcronym(curr_char, next_char)) {
|
||||
// We could be in an acronym at the start of a word. This
|
||||
// is the only case where we actually need to look back at the
|
||||
// previous character, and if that's the case, throw in an
|
||||
// underscore
|
||||
// "SAMLMySAMLAcronymThing");
|
||||
if (between(prev_char, 'a', 'z'))
|
||||
target_inx = setNext('_', rc, target_inx);
|
||||
|
||||
//we are in an acronym - don't snake, just lower
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
} else {
|
||||
target_inx = setNext('_', rc, target_inx);
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
}
|
||||
} else {
|
||||
target_inx = setNext(curr_char, rc, target_inx);
|
||||
}
|
||||
prev_char = curr_char;
|
||||
curr_char = next_char;
|
||||
}
|
||||
// work in the last codepoint - force lowercase
|
||||
target_inx = setNext(lowercase(curr_char), rc, target_inx);
|
||||
|
||||
rc[target_inx] = 0;
|
||||
_ = allocator.resize(rc, target_inx);
|
||||
return rc[0..target_inx];
|
||||
}
|
||||
|
||||
fn isAcronym(char1: u8, char2: u8) bool {
|
||||
return isAcronymChar(char1) and isAcronymChar(char2);
|
||||
}
|
||||
fn isAcronymChar(char: u8) bool {
|
||||
return between(char, 'A', 'Z') or between(char, '0', '9');
|
||||
}
|
||||
fn isAscii(codepoint: ?u21) !?u8 {
|
||||
if (codepoint) |cp| {
|
||||
if (cp > 0xff) return error.UnicodeNotSupported;
|
||||
return @as(u8, @truncate(cp));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn setNext(ascii: u8, slice: []u8, inx: u64) u64 {
|
||||
slice[inx] = ascii;
|
||||
return inx + 1;
|
||||
}
|
||||
|
||||
fn lowercase(ascii: u8) u8 {
|
||||
var lowercase_char = ascii;
|
||||
if (between(ascii, 'A', 'Z'))
|
||||
lowercase_char = ascii + ('a' - 'A');
|
||||
return lowercase_char;
|
||||
}
|
||||
|
||||
fn between(char: u8, from: u8, to: u8) bool {
|
||||
return char >= from and char <= to;
|
||||
}
|
||||
|
||||
test "converts from PascalCase to snake_case" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "MyPascalCaseThing");
|
||||
defer allocator.free(snake_case);
|
||||
try expectEqualStrings("my_pascal_case_thing", snake_case);
|
||||
}
|
||||
test "handles from PascalCase acronyms to snake_case" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "SAMLMySAMLAcronymThing");
|
||||
defer allocator.free(snake_case);
|
||||
try expectEqualStrings("saml_my_saml_acronym_thing", snake_case);
|
||||
}
|
||||
test "spaces in the name" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "API Gateway");
|
||||
defer allocator.free(snake_case);
|
||||
try expectEqualStrings("api_gateway", snake_case);
|
||||
}
|
||||
|
||||
test "S3" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "S3");
|
||||
defer allocator.free(snake_case);
|
||||
try expectEqualStrings("s3", snake_case);
|
||||
}
|
||||
|
||||
test "ec2" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "EC2");
|
||||
defer allocator.free(snake_case);
|
||||
try expectEqualStrings("ec2", snake_case);
|
||||
}
|
||||
|
||||
test "IoT 1Click Devices Service" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "IoT 1Click Devices Service");
|
||||
defer allocator.free(snake_case);
|
||||
// NOTE: There is some debate amoung humans about what this should
|
||||
// turn into. Should it be iot_1click_... or iot_1_click...?
|
||||
try expectEqualStrings("iot_1_click_devices_service", snake_case);
|
||||
}
|
||||
test "Resource Explorer 2" {
|
||||
const allocator = std.testing.allocator;
|
||||
const snake_case = try fromPascalCase(allocator, "Resource Explorer 2");
|
||||
defer allocator.free(snake_case);
|
||||
// NOTE: There is some debate amoung humans about what this should
|
||||
// turn into. Should it be iot_1click_... or iot_1_click...?
|
||||
try expectEqualStrings("resource_explorer_2", snake_case);
|
||||
}
|
Loading…
Add table
Reference in a new issue