Data Types
This commit is contained in:
@@ -28,6 +28,7 @@ type
|
||||
FArrayType: IDataArrayType;
|
||||
FItems: TArray<IDataValue>;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetElementCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
@@ -57,6 +58,27 @@ begin
|
||||
Result := FArrayType;
|
||||
end;
|
||||
|
||||
function TImplDataArrayValue.GetAsString: string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
i: Integer;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('[');
|
||||
for i := 0 to High(FItems) do
|
||||
begin
|
||||
sb.Append(FItems[i].AsString);
|
||||
if (i < High(FItems)) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append(']');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataArrayValue.GetElementCount: Integer;
|
||||
begin
|
||||
Result := Length(FItems);
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
unit Myc.Data.Types.Enum;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Types;
|
||||
|
||||
type
|
||||
TImplDataEnumValue = class(TInterfacedObject, IDataEnumValue)
|
||||
private
|
||||
FDataType: IDataEnumType;
|
||||
FValue: Integer;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetValue: Integer;
|
||||
public
|
||||
constructor Create(const ADataType: IDataEnumType; const AValue: Integer);
|
||||
end;
|
||||
|
||||
TImplDataEnumType = class(TInterfacedObject, IDataEnumType)
|
||||
private
|
||||
FName: string;
|
||||
FIdentifiers: TArray<string>;
|
||||
FIdentifierMap: TDictionary<string, Integer>;
|
||||
function GetName: String;
|
||||
function GetKind: TDataKind;
|
||||
function GetIdentifier(Idx: Integer): string;
|
||||
function GetIdentifierCount: Integer;
|
||||
function IndexOf(const AIdentifier: string): Integer;
|
||||
function CreateValue(const AValue: Integer): IDataEnumValue; overload;
|
||||
function CreateValue(const AIdentifier: string): IDataEnumValue; overload;
|
||||
public
|
||||
constructor Create(const AName: string; const AIdentifiers: array of string);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TImplDataEnumType }
|
||||
|
||||
constructor TImplDataEnumType.Create(const AName: string; const AIdentifiers: array of string);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
inherited Create;
|
||||
FName := AName;
|
||||
FIdentifierMap := TDictionary<string, Integer>.Create;
|
||||
SetLength(FIdentifiers, Length(AIdentifiers));
|
||||
for i := 0 to High(AIdentifiers) do
|
||||
begin
|
||||
if FIdentifierMap.ContainsKey(AIdentifiers[i]) then
|
||||
raise EArgumentException.CreateFmt('Duplicate identifier: %s', [AIdentifiers[i]]);
|
||||
FIdentifiers[i] := AIdentifiers[i];
|
||||
FIdentifierMap.Add(AIdentifiers[i], i);
|
||||
end;
|
||||
end;
|
||||
|
||||
destructor TImplDataEnumType.Destroy;
|
||||
begin
|
||||
FIdentifierMap.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.GetName: String;
|
||||
begin
|
||||
Result := FName;
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.GetKind: TDataKind;
|
||||
begin
|
||||
Result := dkEnum;
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.GetIdentifier(Idx: Integer): string;
|
||||
begin
|
||||
Result := FIdentifiers[Idx];
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.GetIdentifierCount: Integer;
|
||||
begin
|
||||
Result := Length(FIdentifiers);
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.IndexOf(const AIdentifier: string): Integer;
|
||||
begin
|
||||
if not FIdentifierMap.TryGetValue(AIdentifier, Result) then
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.CreateValue(const AValue: Integer): IDataEnumValue;
|
||||
begin
|
||||
if (AValue < 0) or (AValue >= Length(FIdentifiers)) then
|
||||
raise EArgumentException.Create('Invalid enum value');
|
||||
Result := TImplDataEnumValue.Create(Self, AValue);
|
||||
end;
|
||||
|
||||
function TImplDataEnumType.CreateValue(const AIdentifier: string): IDataEnumValue;
|
||||
var
|
||||
idx: Integer;
|
||||
begin
|
||||
idx := IndexOf(AIdentifier);
|
||||
if idx < 0 then
|
||||
raise EArgumentException.CreateFmt('Unknown identifier: %s', [AIdentifier]);
|
||||
Result := CreateValue(idx);
|
||||
end;
|
||||
|
||||
{ TImplDataEnumValue }
|
||||
|
||||
constructor TImplDataEnumValue.Create(const ADataType: IDataEnumType; const AValue: Integer);
|
||||
begin
|
||||
FDataType := ADataType;
|
||||
FValue := AValue;
|
||||
end;
|
||||
|
||||
function TImplDataEnumValue.GetDataType: IDataType;
|
||||
begin
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataEnumValue.GetAsString: string;
|
||||
begin
|
||||
Result := FDataType.Identifiers[FValue];
|
||||
end;
|
||||
|
||||
function TImplDataEnumValue.GetValue: Integer;
|
||||
begin
|
||||
Result := FValue;
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -16,6 +16,7 @@ type
|
||||
|
||||
// IDataValue
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
|
||||
// IDataFloatValue
|
||||
function GetValue: Double;
|
||||
@@ -49,6 +50,11 @@ begin
|
||||
Result := TImplDataFloatType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataFloatValue.GetAsString: string;
|
||||
begin
|
||||
Result := FloatToStr(FValue);
|
||||
end;
|
||||
|
||||
function TImplDataFloatValue.GetValue: Double;
|
||||
begin
|
||||
Result := FValue;
|
||||
|
||||
@@ -16,6 +16,7 @@ type
|
||||
|
||||
// IDataValue
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
|
||||
// IDataOrdinalValue
|
||||
function GetValue: Int64;
|
||||
@@ -49,6 +50,11 @@ begin
|
||||
Result := TImplDataOrdinalType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataOrdinalValue.GetAsString: string;
|
||||
begin
|
||||
Result := FValue.ToString;
|
||||
end;
|
||||
|
||||
function TImplDataOrdinalValue.GetValue: Int64;
|
||||
begin
|
||||
Result := FValue;
|
||||
|
||||
@@ -41,6 +41,7 @@ type
|
||||
FDataType: IDataRecordType;
|
||||
FItems: TArray<IDataValue>;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
|
||||
@@ -97,6 +98,31 @@ begin
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataRecordValue.GetAsString: string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
i: Integer;
|
||||
recordType: IDataRecordType;
|
||||
begin
|
||||
recordType := FDataType;
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('<');
|
||||
for i := 0 to High(FItems) do
|
||||
begin
|
||||
sb.Append(recordType.Fields[i].Name);
|
||||
sb.Append(': ');
|
||||
sb.Append(FItems[i].AsString);
|
||||
if (i < High(FItems)) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append('>');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataRecordValue.GetItem(Idx: Integer): IDataValue;
|
||||
begin
|
||||
Result := FItems[Idx];
|
||||
|
||||
@@ -23,6 +23,7 @@ type
|
||||
private
|
||||
FValue: string;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetValue: string;
|
||||
public
|
||||
constructor Create(const AValue: string);
|
||||
@@ -65,6 +66,11 @@ begin
|
||||
Result := TImplDataTextType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataTextValue.GetAsString: string;
|
||||
begin
|
||||
Result := FValue;
|
||||
end;
|
||||
|
||||
function TImplDataTextValue.GetValue: string;
|
||||
begin
|
||||
Result := FValue;
|
||||
|
||||
@@ -23,6 +23,7 @@ type
|
||||
private
|
||||
FValue: TDateTime;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetValue: TDateTime;
|
||||
public
|
||||
constructor Create(const AValue: TDateTime);
|
||||
@@ -65,6 +66,11 @@ begin
|
||||
Result := TImplDataTimestampType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataTimestampValue.GetAsString: string;
|
||||
begin
|
||||
Result := DateTimeToStr(FValue);
|
||||
end;
|
||||
|
||||
function TImplDataTimestampValue.GetValue: TDateTime;
|
||||
begin
|
||||
Result := FValue;
|
||||
|
||||
@@ -12,6 +12,7 @@ type
|
||||
private
|
||||
FItems: TArray<IDataValue>;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
@@ -51,6 +52,27 @@ begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue.GetAsString: string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
i: Integer;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('(');
|
||||
for i := 0 to High(FItems) do
|
||||
begin
|
||||
sb.Append(FItems[i].AsString);
|
||||
if (i < High(FItems)) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append(')');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue.GetItem(Idx: Integer): IDataValue;
|
||||
begin
|
||||
Result := FItems[Idx];
|
||||
|
||||
@@ -7,7 +7,7 @@ uses
|
||||
System.SysUtils;
|
||||
|
||||
type
|
||||
TDataKind = (dkOrdinal, dkFloat, dkText, dkTimestamp, dkRecord, dkTuple, dkArray);
|
||||
TDataKind = (dkOrdinal, dkFloat, dkText, dkTimestamp, dkRecord, dkTuple, dkArray, dkEnum);
|
||||
|
||||
IDataType = interface
|
||||
function GetName: String;
|
||||
@@ -18,7 +18,9 @@ type
|
||||
|
||||
IDataValue = interface
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
property DataType: IDataType read GetDataType;
|
||||
property AsString: string read GetAsString;
|
||||
end;
|
||||
|
||||
IDataOrdinalValue = interface(IDataValue)
|
||||
@@ -57,6 +59,21 @@ type
|
||||
function CreateValue(const AValue: TDateTime): IDataTimestampValue;
|
||||
end;
|
||||
|
||||
IDataEnumValue = interface(IDataValue)
|
||||
function GetValue: Integer;
|
||||
property Value: Integer read GetValue;
|
||||
end;
|
||||
|
||||
IDataEnumType = interface(IDataType)
|
||||
function GetIdentifier(Idx: Integer): string;
|
||||
function GetIdentifierCount: Integer;
|
||||
function IndexOf(const AIdentifier: string): Integer;
|
||||
function CreateValue(const AValue: Integer): IDataEnumValue; overload;
|
||||
function CreateValue(const AIdentifier: string): IDataEnumValue; overload;
|
||||
property IdentifierCount: Integer read GetIdentifierCount;
|
||||
property Identifiers[Idx: Integer]: string read GetIdentifier; default;
|
||||
end;
|
||||
|
||||
IDataRecordValue = interface(IDataValue)
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
property Items[Idx: Integer]: IDataValue read GetItem; default;
|
||||
@@ -127,6 +144,7 @@ type
|
||||
class function ArrayOfType(const AElementType: IDataType): IDataArrayType; static;
|
||||
class function RecordOf(const Fields: TArray<TRecordField>): IDataRecordType; overload; static;
|
||||
class function RecordOf(const Fields: array of TRecordField): IDataRecordType; overload; static;
|
||||
class function EnumOf(const AName: string; const AIdentifiers: array of string): IDataEnumType; static;
|
||||
|
||||
property DataType: IDataType read FDataType;
|
||||
property Name: String read GetName;
|
||||
@@ -147,6 +165,7 @@ type
|
||||
function AsRecord: IDataRecordValue;
|
||||
function AsTuple: IDataTupleValue;
|
||||
function AsArray: IDataArrayValue;
|
||||
function AsEnum: IDataEnumValue;
|
||||
|
||||
class operator Implicit(const A: IDataValue): TDataValue; overload;
|
||||
class operator Implicit(const A: TDataValue): IDataValue; overload;
|
||||
@@ -173,7 +192,8 @@ uses
|
||||
Myc.Data.Types.Timestamp,
|
||||
Myc.Data.Types.Arrays,
|
||||
Myc.Data.Types.Records,
|
||||
Myc.Data.Types.Tuple;
|
||||
Myc.Data.Types.Tuple,
|
||||
Myc.Data.Types.Enum;
|
||||
|
||||
{ TRecordField }
|
||||
|
||||
@@ -262,6 +282,11 @@ begin
|
||||
Result := RecordOf(tFields);
|
||||
end;
|
||||
|
||||
class function TDataType.EnumOf(const AName: string; const AIdentifiers: array of string): IDataEnumType;
|
||||
begin
|
||||
Result := TImplDataEnumType.Create(AName, AIdentifiers);
|
||||
end;
|
||||
|
||||
class function TDataType.Text: IDataTextType;
|
||||
begin
|
||||
Result := TImplDataTextType.Singleton;
|
||||
@@ -343,6 +368,13 @@ begin
|
||||
Result := IDataTupleValue(FDataValue);
|
||||
end;
|
||||
|
||||
function TDataValue.AsEnum: IDataEnumValue;
|
||||
begin
|
||||
if FDataValue.DataType.Kind <> dkEnum then
|
||||
raise EInvalidCast.Create('Enum expected');
|
||||
Result := IDataEnumValue(FDataValue);
|
||||
end;
|
||||
|
||||
class function TDataValue.FromOrdinal(const AValue: Int64): IDataOrdinalValue;
|
||||
begin
|
||||
Result := TImplDataOrdinalType.Singleton.CreateValue(AValue);
|
||||
|
||||
+114
-1
@@ -23,6 +23,12 @@ type
|
||||
|
||||
[Test]
|
||||
procedure TestTimestamps;
|
||||
|
||||
[Test]
|
||||
procedure TestEnums;
|
||||
|
||||
[Test]
|
||||
procedure TestAsString;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -253,7 +259,7 @@ begin
|
||||
Assert.AreEqual(dkTimestamp, tsType.Kind, 'Type kind should be dkTimestamp');
|
||||
|
||||
// --- 2. Test Value Creation and Access ---
|
||||
now := Now;
|
||||
now := System.Sysutils.Now;
|
||||
tsValue1 := TDataValue.FromTimestamp(now);
|
||||
|
||||
Assert.IsNotNull(tsValue1, 'TimestampValue should be created');
|
||||
@@ -277,6 +283,113 @@ begin
|
||||
);
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestEnums;
|
||||
var
|
||||
colorType: IDataEnumType;
|
||||
red, green, blue: IDataEnumValue;
|
||||
begin
|
||||
// --- 1. Test Type Creation ---
|
||||
colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']);
|
||||
|
||||
Assert.IsNotNull(colorType, 'EnumType should be created');
|
||||
Assert.AreEqual('Color', colorType.Name, 'Name should be Color');
|
||||
Assert.AreEqual(dkEnum, colorType.Kind, 'Kind should be dkEnum');
|
||||
Assert.AreEqual(3, colorType.IdentifierCount, 'IdentifierCount should be 3');
|
||||
Assert.AreEqual('Red', colorType.Identifiers[0], 'Identifier at index 0 should be Red');
|
||||
Assert.AreEqual('Green', colorType.Identifiers[1], 'Identifier at index 1 should be Green');
|
||||
Assert.AreEqual('Blue', colorType.Identifiers[2], 'Identifier at index 2 should be Blue');
|
||||
Assert.AreEqual(1, colorType.IndexOf('Green'), 'IndexOf Green should be 1');
|
||||
Assert.AreEqual(-1, colorType.IndexOf('Yellow'), 'IndexOf Yellow should be -1');
|
||||
|
||||
// --- 2. Test Value Creation and Access ---
|
||||
red := colorType.CreateValue(0);
|
||||
green := colorType.CreateValue('Green');
|
||||
blue := TDataValue(colorType.CreateValue(2)).AsEnum;
|
||||
|
||||
Assert.IsNotNull(red, 'EnumValue from index should be created');
|
||||
Assert.AreSame(colorType, red.DataType, 'Red should have the correct data type');
|
||||
Assert.AreEqual(0, red.Value, 'Value of Red should be 0');
|
||||
Assert.AreEqual('Red', red.AsString, 'AsText of Red should be Red');
|
||||
|
||||
Assert.IsNotNull(green, 'EnumValue from identifier should be created');
|
||||
Assert.AreSame(colorType, green.DataType, 'Green should have the correct data type');
|
||||
Assert.AreEqual(1, green.Value, 'Value of Green should be 1');
|
||||
Assert.AreEqual('Green', green.AsString, 'AsText of Green should be Green');
|
||||
|
||||
Assert.AreEqual(2, blue.Value, 'Value of Blue should be 2');
|
||||
|
||||
// --- 3. Test Validation and Error Handling ---
|
||||
// Test creating a type with duplicate identifiers
|
||||
Assert.WillRaise(
|
||||
procedure begin TDataType.EnumOf('Fails', ['A', 'B', 'A']); end,
|
||||
EArgumentException,
|
||||
'Duplicate identifiers should raise an exception'
|
||||
);
|
||||
|
||||
// Test creating a value with an invalid index
|
||||
Assert.WillRaise(procedure begin colorType.CreateValue(3); end, EArgumentException, 'Invalid index should raise an exception');
|
||||
Assert.WillRaise(procedure begin colorType.CreateValue(-1); end, EArgumentException, 'Negative index should raise an exception');
|
||||
|
||||
// Test creating a value with an unknown identifier
|
||||
Assert.WillRaise(
|
||||
procedure begin colorType.CreateValue('Yellow'); end,
|
||||
EArgumentException,
|
||||
'Unknown identifier should raise an exception'
|
||||
);
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestAsString;
|
||||
var
|
||||
ordinalValue: IDataOrdinalValue;
|
||||
floatValue: IDataFloatValue;
|
||||
textValue: IDataTextValue;
|
||||
tsValue: IDataTimestampValue;
|
||||
enumValue: IDataEnumValue;
|
||||
arrayValue: IDataArrayValue;
|
||||
recordValue: IDataRecordValue;
|
||||
tupleValue: IDataTupleValue;
|
||||
colorType: IDataEnumType;
|
||||
personType: IDataRecordType;
|
||||
intArrayType: IDataArrayType;
|
||||
now: TDateTime;
|
||||
begin
|
||||
// Ordinal
|
||||
ordinalValue := TDataValue.FromOrdinal(123);
|
||||
Assert.AreEqual('123', ordinalValue.AsString, 'Ordinal AsString incorrect');
|
||||
|
||||
// Float
|
||||
floatValue := TDataValue.FromFloat(45.67);
|
||||
Assert.AreEqual(FloatToStr(45.67), floatValue.AsString, 'Float AsString incorrect');
|
||||
|
||||
// Text
|
||||
textValue := TDataValue.FromText('Hello');
|
||||
Assert.AreEqual('Hello', textValue.AsString, 'Text AsString incorrect');
|
||||
|
||||
// Timestamp
|
||||
now := System.Sysutils.Now;
|
||||
tsValue := TDataValue.FromTimestamp(now);
|
||||
Assert.AreEqual(DateTimeToStr(now), tsValue.AsString, 'Timestamp AsString incorrect');
|
||||
|
||||
// Enum
|
||||
colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']);
|
||||
enumValue := colorType.CreateValue('Green');
|
||||
Assert.AreEqual('Green', enumValue.AsString, 'Enum AsString incorrect');
|
||||
|
||||
// Array
|
||||
intArrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
||||
arrayValue := intArrayType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromOrdinal(2)]);
|
||||
Assert.AreEqual('[1, 2]', arrayValue.AsString, 'Array AsString incorrect');
|
||||
|
||||
// Record
|
||||
personType := TDataType.RecordOf([TRecordField.Create('ID', TDataType.Ordinal), TRecordField.Create('Name', TDataType.Text)]);
|
||||
recordValue := personType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromText('Bob')]);
|
||||
Assert.AreEqual('<ID: 1, Name: Bob>', recordValue.AsString, 'Record AsString incorrect');
|
||||
|
||||
// Tuple
|
||||
tupleValue := TDataValue.FromTuple([TDataValue.FromOrdinal(10), TDataValue.FromText('Tuple')]);
|
||||
Assert.AreEqual('(10, Tuple)', tupleValue.AsString, 'Tuple AsString incorrect');
|
||||
end;
|
||||
|
||||
initialization
|
||||
TDUnitX.RegisterTestFixture(TMyTestObject);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user