first commit
This commit is contained in:
450
dynamodb/types.odin
Normal file
450
dynamodb/types.odin
Normal file
@@ -0,0 +1,450 @@
|
||||
package dynamodb
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
|
||||
// DynamoDB AttributeValue - the core data type
|
||||
Attribute_Value :: union {
|
||||
String, // S
|
||||
Number, // N (stored as string)
|
||||
Binary, // B (base64)
|
||||
Bool, // BOOL
|
||||
Null, // NULL
|
||||
String_Set, // SS
|
||||
Number_Set, // NS
|
||||
Binary_Set, // BS
|
||||
List, // L
|
||||
Map, // M
|
||||
}
|
||||
|
||||
String :: distinct string
|
||||
Number :: distinct string
|
||||
Binary :: distinct string
|
||||
Bool :: distinct bool
|
||||
Null :: distinct bool
|
||||
|
||||
String_Set :: distinct []string
|
||||
Number_Set :: distinct []string
|
||||
Binary_Set :: distinct []string
|
||||
List :: distinct []Attribute_Value
|
||||
Map :: distinct map[string]Attribute_Value
|
||||
|
||||
// Item is a map of attribute names to values
|
||||
Item :: map[string]Attribute_Value
|
||||
|
||||
// Key represents a DynamoDB key (partition key + optional sort key)
|
||||
Key :: struct {
|
||||
pk: Attribute_Value,
|
||||
sk: Maybe(Attribute_Value),
|
||||
}
|
||||
|
||||
// Free a key
|
||||
key_destroy :: proc(key: ^Key) {
|
||||
attr_value_destroy(&key.pk)
|
||||
if sk, ok := key.sk.?; ok {
|
||||
sk_copy := sk
|
||||
attr_value_destroy(&sk_copy)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract key from item based on key schema
|
||||
key_from_item :: proc(item: Item, key_schema: []Key_Schema_Element) -> (Key, bool) {
|
||||
pk_value: Attribute_Value
|
||||
sk_value: Maybe(Attribute_Value)
|
||||
|
||||
for schema_elem in key_schema {
|
||||
attr, ok := item[schema_elem.attribute_name]
|
||||
if !ok {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
// Validate that key is a scalar type (S, N, or B)
|
||||
#partial switch _ in attr {
|
||||
case String, Number, Binary:
|
||||
// Valid key type
|
||||
case:
|
||||
return {}, false
|
||||
}
|
||||
|
||||
// Deep copy the attribute value
|
||||
copied := attr_value_deep_copy(attr)
|
||||
|
||||
switch schema_elem.key_type {
|
||||
case .HASH:
|
||||
pk_value = copied
|
||||
case .RANGE:
|
||||
sk_value = copied
|
||||
}
|
||||
}
|
||||
|
||||
return Key{pk = pk_value, sk = sk_value}, true
|
||||
}
|
||||
|
||||
// Convert key to item
|
||||
key_to_item :: proc(key: Key, key_schema: []Key_Schema_Element) -> Item {
|
||||
item := make(Item)
|
||||
|
||||
for schema_elem in key_schema {
|
||||
attr_value: Attribute_Value
|
||||
|
||||
switch schema_elem.key_type {
|
||||
case .HASH:
|
||||
attr_value = key.pk
|
||||
case .RANGE:
|
||||
if sk, ok := key.sk.?; ok {
|
||||
attr_value = sk
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
item[schema_elem.attribute_name] = attr_value_deep_copy(attr_value)
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
// Extract raw byte values from key
|
||||
Key_Values :: struct {
|
||||
pk: []byte,
|
||||
sk: Maybe([]byte),
|
||||
}
|
||||
|
||||
key_get_values :: proc(key: ^Key) -> (Key_Values, bool) {
|
||||
pk_bytes: []byte
|
||||
|
||||
switch v in key.pk {
|
||||
case String:
|
||||
pk_bytes = transmute([]byte)string(v)
|
||||
case Number:
|
||||
pk_bytes = transmute([]byte)string(v)
|
||||
case Binary:
|
||||
pk_bytes = transmute([]byte)string(v)
|
||||
case:
|
||||
return {}, false
|
||||
}
|
||||
|
||||
sk_bytes: Maybe([]byte)
|
||||
if sk, ok := key.sk.?; ok {
|
||||
switch v in sk {
|
||||
case String:
|
||||
sk_bytes = transmute([]byte)string(v)
|
||||
case Number:
|
||||
sk_bytes = transmute([]byte)string(v)
|
||||
case Binary:
|
||||
sk_bytes = transmute([]byte)string(v)
|
||||
case:
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
|
||||
return Key_Values{pk = pk_bytes, sk = sk_bytes}, true
|
||||
}
|
||||
|
||||
// Key type
|
||||
Key_Type :: enum {
|
||||
HASH,
|
||||
RANGE,
|
||||
}
|
||||
|
||||
key_type_to_string :: proc(kt: Key_Type) -> string {
|
||||
switch kt {
|
||||
case .HASH: return "HASH"
|
||||
case .RANGE: return "RANGE"
|
||||
}
|
||||
return "HASH"
|
||||
}
|
||||
|
||||
key_type_from_string :: proc(s: string) -> (Key_Type, bool) {
|
||||
switch s {
|
||||
case "HASH": return .HASH, true
|
||||
case "RANGE": return .RANGE, true
|
||||
}
|
||||
return .HASH, false
|
||||
}
|
||||
|
||||
// Scalar attribute type
|
||||
Scalar_Attribute_Type :: enum {
|
||||
S, // String
|
||||
N, // Number
|
||||
B, // Binary
|
||||
}
|
||||
|
||||
scalar_type_to_string :: proc(t: Scalar_Attribute_Type) -> string {
|
||||
switch t {
|
||||
case .S: return "S"
|
||||
case .N: return "N"
|
||||
case .B: return "B"
|
||||
}
|
||||
return "S"
|
||||
}
|
||||
|
||||
scalar_type_from_string :: proc(s: string) -> (Scalar_Attribute_Type, bool) {
|
||||
switch s {
|
||||
case "S": return .S, true
|
||||
case "N": return .N, true
|
||||
case "B": return .B, true
|
||||
}
|
||||
return .S, false
|
||||
}
|
||||
|
||||
// Key schema element
|
||||
Key_Schema_Element :: struct {
|
||||
attribute_name: string,
|
||||
key_type: Key_Type,
|
||||
}
|
||||
|
||||
// Attribute definition
|
||||
Attribute_Definition :: struct {
|
||||
attribute_name: string,
|
||||
attribute_type: Scalar_Attribute_Type,
|
||||
}
|
||||
|
||||
// Projection type for indexes
|
||||
Projection_Type :: enum {
|
||||
ALL,
|
||||
KEYS_ONLY,
|
||||
INCLUDE,
|
||||
}
|
||||
|
||||
// Projection
|
||||
Projection :: struct {
|
||||
projection_type: Projection_Type,
|
||||
non_key_attributes: Maybe([]string),
|
||||
}
|
||||
|
||||
// Global secondary index
|
||||
Global_Secondary_Index :: struct {
|
||||
index_name: string,
|
||||
key_schema: []Key_Schema_Element,
|
||||
projection: Projection,
|
||||
}
|
||||
|
||||
// Local secondary index
|
||||
Local_Secondary_Index :: struct {
|
||||
index_name: string,
|
||||
key_schema: []Key_Schema_Element,
|
||||
projection: Projection,
|
||||
}
|
||||
|
||||
// Table status
|
||||
Table_Status :: enum {
|
||||
CREATING,
|
||||
UPDATING,
|
||||
DELETING,
|
||||
ACTIVE,
|
||||
INACCESSIBLE_ENCRYPTION_CREDENTIALS,
|
||||
ARCHIVING,
|
||||
ARCHIVED,
|
||||
}
|
||||
|
||||
table_status_to_string :: proc(status: Table_Status) -> string {
|
||||
switch status {
|
||||
case .CREATING: return "CREATING"
|
||||
case .UPDATING: return "UPDATING"
|
||||
case .DELETING: return "DELETING"
|
||||
case .ACTIVE: return "ACTIVE"
|
||||
case .INACCESSIBLE_ENCRYPTION_CREDENTIALS: return "INACCESSIBLE_ENCRYPTION_CREDENTIALS"
|
||||
case .ARCHIVING: return "ARCHIVING"
|
||||
case .ARCHIVED: return "ARCHIVED"
|
||||
}
|
||||
return "ACTIVE"
|
||||
}
|
||||
|
||||
// Table description
|
||||
Table_Description :: struct {
|
||||
table_name: string,
|
||||
key_schema: []Key_Schema_Element,
|
||||
attribute_definitions: []Attribute_Definition,
|
||||
table_status: Table_Status,
|
||||
creation_date_time: i64,
|
||||
item_count: u64,
|
||||
table_size_bytes: u64,
|
||||
global_secondary_indexes: Maybe([]Global_Secondary_Index),
|
||||
local_secondary_indexes: Maybe([]Local_Secondary_Index),
|
||||
}
|
||||
|
||||
// DynamoDB operation types
|
||||
Operation :: enum {
|
||||
CreateTable,
|
||||
DeleteTable,
|
||||
DescribeTable,
|
||||
ListTables,
|
||||
UpdateTable,
|
||||
PutItem,
|
||||
GetItem,
|
||||
DeleteItem,
|
||||
UpdateItem,
|
||||
Query,
|
||||
Scan,
|
||||
BatchGetItem,
|
||||
BatchWriteItem,
|
||||
TransactGetItems,
|
||||
TransactWriteItems,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
operation_from_target :: proc(target: string) -> Operation {
|
||||
prefix :: "DynamoDB_20120810."
|
||||
if !strings.has_prefix(target, prefix) {
|
||||
return .Unknown
|
||||
}
|
||||
|
||||
op_name := target[len(prefix):]
|
||||
|
||||
switch op_name {
|
||||
case "CreateTable": return .CreateTable
|
||||
case "DeleteTable": return .DeleteTable
|
||||
case "DescribeTable": return .DescribeTable
|
||||
case "ListTables": return .ListTables
|
||||
case "UpdateTable": return .UpdateTable
|
||||
case "PutItem": return .PutItem
|
||||
case "GetItem": return .GetItem
|
||||
case "DeleteItem": return .DeleteItem
|
||||
case "UpdateItem": return .UpdateItem
|
||||
case "Query": return .Query
|
||||
case "Scan": return .Scan
|
||||
case "BatchGetItem": return .BatchGetItem
|
||||
case "BatchWriteItem": return .BatchWriteItem
|
||||
case "TransactGetItems": return .TransactGetItems
|
||||
case "TransactWriteItems": return .TransactWriteItems
|
||||
}
|
||||
|
||||
return .Unknown
|
||||
}
|
||||
|
||||
// DynamoDB error types
|
||||
DynamoDB_Error_Type :: enum {
|
||||
ValidationException,
|
||||
ResourceNotFoundException,
|
||||
ResourceInUseException,
|
||||
ConditionalCheckFailedException,
|
||||
ProvisionedThroughputExceededException,
|
||||
ItemCollectionSizeLimitExceededException,
|
||||
InternalServerError,
|
||||
SerializationException,
|
||||
}
|
||||
|
||||
error_to_response :: proc(err_type: DynamoDB_Error_Type, message: string) -> string {
|
||||
type_str: string
|
||||
|
||||
switch err_type {
|
||||
case .ValidationException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ValidationException"
|
||||
case .ResourceNotFoundException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
|
||||
case .ResourceInUseException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ResourceInUseException"
|
||||
case .ConditionalCheckFailedException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException"
|
||||
case .ProvisionedThroughputExceededException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ProvisionedThroughputExceededException"
|
||||
case .ItemCollectionSizeLimitExceededException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#ItemCollectionSizeLimitExceededException"
|
||||
case .InternalServerError:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#InternalServerError"
|
||||
case .SerializationException:
|
||||
type_str = "com.amazonaws.dynamodb.v20120810#SerializationException"
|
||||
}
|
||||
|
||||
return fmt.aprintf(`{{"__type":"%s","message":"%s"}}`, type_str, message)
|
||||
}
|
||||
|
||||
// Deep copy an attribute value
|
||||
attr_value_deep_copy :: proc(attr: Attribute_Value) -> Attribute_Value {
|
||||
switch v in attr {
|
||||
case String:
|
||||
return String(strings.clone(string(v)))
|
||||
case Number:
|
||||
return Number(strings.clone(string(v)))
|
||||
case Binary:
|
||||
return Binary(strings.clone(string(v)))
|
||||
case Bool:
|
||||
return v
|
||||
case Null:
|
||||
return v
|
||||
case String_Set:
|
||||
ss := make([]string, len(v))
|
||||
for s, i in v {
|
||||
ss[i] = strings.clone(s)
|
||||
}
|
||||
return String_Set(ss)
|
||||
case Number_Set:
|
||||
ns := make([]string, len(v))
|
||||
for n, i in v {
|
||||
ns[i] = strings.clone(n)
|
||||
}
|
||||
return Number_Set(ns)
|
||||
case Binary_Set:
|
||||
bs := make([]string, len(v))
|
||||
for b, i in v {
|
||||
bs[i] = strings.clone(b)
|
||||
}
|
||||
return Binary_Set(bs)
|
||||
case List:
|
||||
list := make([]Attribute_Value, len(v))
|
||||
for item, i in v {
|
||||
list[i] = attr_value_deep_copy(item)
|
||||
}
|
||||
return List(list)
|
||||
case Map:
|
||||
m := make(map[string]Attribute_Value)
|
||||
for key, val in v {
|
||||
m[strings.clone(key)] = attr_value_deep_copy(val)
|
||||
}
|
||||
return Map(m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Free an attribute value
|
||||
attr_value_destroy :: proc(attr: ^Attribute_Value) {
|
||||
switch v in attr {
|
||||
case String:
|
||||
delete(string(v))
|
||||
case Number:
|
||||
delete(string(v))
|
||||
case Binary:
|
||||
delete(string(v))
|
||||
case String_Set:
|
||||
for s in v {
|
||||
delete(s)
|
||||
}
|
||||
delete([]string(v))
|
||||
case Number_Set:
|
||||
for n in v {
|
||||
delete(n)
|
||||
}
|
||||
delete([]string(v))
|
||||
case Binary_Set:
|
||||
for b in v {
|
||||
delete(b)
|
||||
}
|
||||
delete([]string(v))
|
||||
case List:
|
||||
for item in v {
|
||||
item_copy := item
|
||||
attr_value_destroy(&item_copy)
|
||||
}
|
||||
delete([]Attribute_Value(v))
|
||||
case Map:
|
||||
for key, val in v {
|
||||
delete(key)
|
||||
val_copy := val
|
||||
attr_value_destroy(&val_copy)
|
||||
}
|
||||
delete(map[string]Attribute_Value(v))
|
||||
case Bool, Null:
|
||||
// Nothing to free
|
||||
}
|
||||
}
|
||||
|
||||
// Free an item
|
||||
item_destroy :: proc(item: ^Item) {
|
||||
for key, val in item {
|
||||
delete(key)
|
||||
val_copy := val
|
||||
attr_value_destroy(&val_copy)
|
||||
}
|
||||
delete(item^)
|
||||
}
|
||||
Reference in New Issue
Block a user