diff --git a/Libraries/Esiur/Data/DataSerializer.cs b/Libraries/Esiur/Data/DataSerializer.cs index 92ccc66..8a160c1 100644 --- a/Libraries/Esiur/Data/DataSerializer.cs +++ b/Libraries/Esiur/Data/DataSerializer.cs @@ -1,6 +1,7 @@ using Esiur.Core; using Esiur.Data.Gvwie; using Esiur.Data.Types; +using Esiur.Net.Packets; using Esiur.Protocol; using Esiur.Resource; using Microsoft.CodeAnalysis; @@ -9,6 +10,7 @@ using System.Buffers.Binary; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using System.Text; namespace Esiur.Data; @@ -980,10 +982,7 @@ public static class DataSerializer } - public static Tdu TypeDefComposer(object value, Warehouse warehouse, EpConnection connection) - { - } } diff --git a/Libraries/Esiur/Data/OrderingControl.cs b/Libraries/Esiur/Data/OrderingControl.cs new file mode 100644 index 0000000..72aad29 --- /dev/null +++ b/Libraries/Esiur/Data/OrderingControl.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public enum OrderingControl : byte + { + Strict = 0, + + // Can be reordered with unrelated notifications, + // but updates are still delivered. + Relaxed = 1, + + // Runtime may keep only the newest pending value + // for this property. + LatestOnly = 2, + } +} diff --git a/Libraries/Esiur/Data/Types/ArgumentDef.cs b/Libraries/Esiur/Data/Types/ArgumentDef.cs index 166acac..6b80798 100644 --- a/Libraries/Esiur/Data/Types/ArgumentDef.cs +++ b/Libraries/Esiur/Data/Types/ArgumentDef.cs @@ -23,6 +23,11 @@ public class ArgumentDef public Map Annotations { get; set; } + + //public ArgumentDefFlags Flags { get; set; } + + public object DefaultValue { get; set; } + public static async AsyncReply> ParseAsync(byte[] data, uint offset, int index, EpConnection connection, ulong[] requestSequence) { var optional = (data[offset] & 0x1) == 0x1; diff --git a/Libraries/Esiur/Data/Types/ArgumentDefField.cs b/Libraries/Esiur/Data/Types/ArgumentDefField.cs new file mode 100644 index 0000000..5c6a73f --- /dev/null +++ b/Libraries/Esiur/Data/Types/ArgumentDefField.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum ArgumentDefField : byte + { + ValueType = 0x03, // TypeRepresentation + DefaultValue = 0x04, // object + } +} diff --git a/Libraries/Esiur/Data/Types/ArgumentDefFlags.cs b/Libraries/Esiur/Data/Types/ArgumentDefFlags.cs new file mode 100644 index 0000000..726bbab --- /dev/null +++ b/Libraries/Esiur/Data/Types/ArgumentDefFlags.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum ArgumentDefFlags : byte + { + None = 0, + Optional = 0x01, // Caller may omit the argument. + Variadic = 0x02, // Last argument accepts multiple values. + } +} diff --git a/Libraries/Esiur/Data/Types/AttributeDef.cs b/Libraries/Esiur/Data/Types/AttributeDef.cs index aa6a7ca..e15985c 100644 --- a/Libraries/Esiur/Data/Types/AttributeDef.cs +++ b/Libraries/Esiur/Data/Types/AttributeDef.cs @@ -17,7 +17,7 @@ public class AttributeDef : MemberDef set; } - + public static AttributeDef MakeAttributeDef(Type type, PropertyInfo pi, byte index, string name, TypeDef typeDef) { return new AttributeDef() diff --git a/Libraries/Esiur/Data/Types/ConstantDefField.cs b/Libraries/Esiur/Data/Types/ConstantDefField.cs new file mode 100644 index 0000000..d3e2937 --- /dev/null +++ b/Libraries/Esiur/Data/Types/ConstantDefField.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum ConstantDefField : byte + { + ValueType = 0x03, // TypeRepresentation + Value = 0x04, // object + } +} diff --git a/Libraries/Esiur/Data/Types/ConstantDefFlags.cs b/Libraries/Esiur/Data/Types/ConstantDefFlags.cs new file mode 100644 index 0000000..3dd33b2 --- /dev/null +++ b/Libraries/Esiur/Data/Types/ConstantDefFlags.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum ConstantDefFlags : byte + { + None = 0x00, + Inherited = (byte)MemberDefFlags.Inherited, + Deprecated = (byte)MemberDefFlags.Deprecated, + } +} diff --git a/Libraries/Esiur/Data/Types/EventDef.cs b/Libraries/Esiur/Data/Types/EventDef.cs index 7799765..a6ec2c5 100644 --- a/Libraries/Esiur/Data/Types/EventDef.cs +++ b/Libraries/Esiur/Data/Types/EventDef.cs @@ -26,83 +26,86 @@ public class EventDef : MemberDef return $"{Name}: {ArgumentType}"; } - public bool Subscribable { get; set; } + public bool AutoDelivered { get; set; } + + public bool Deprecated { get; set; } public EventInfo EventInfo { get; set; } public Tru ArgumentType { get; set; } + - public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) - { - var oOffset = offset; + //public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) + //{ + // var oOffset = offset; - var hasAnnotation = ((data[offset] & 0x10) == 0x10); - var subscribable = ((data[offset++] & 0x8) == 0x8); + // var hasAnnotation = ((data[offset] & 0x10) == 0x10); + // var subscribable = ((data[offset++] & 0x8) == 0x8); - var name = data.GetString(offset + 1, data[offset]); - offset += (uint)data[offset] + 1; + // var name = data.GetString(offset + 1, data[offset]); + // offset += (uint)data[offset] + 1; - var argType = await Tru.ParseAsync(data, offset, connection, requestSequence); + // var argType = await Tru.ParseAsync(data, offset, connection, requestSequence); - offset += argType.Size; + // offset += argType.Size; - // Annotation ? - Map annotations = null; + // // Annotation ? + // Map annotations = null; - if (hasAnnotation) - { - var (len, anns) = Codec.ParseSync(data, offset, null); + // if (hasAnnotation) + // { + // var (len, anns) = Codec.ParseSync(data, offset, null); - if (anns is Map map) - annotations = map; + // if (anns is Map map) + // annotations = map; - offset += len; - } + // offset += len; + // } - return new ParseResult(new EventDef() - { - Index = index, - Name = name, - Inherited = inherited, - ArgumentType = argType.Value, - Subscribable = subscribable, - Annotations = annotations - }, offset - oOffset); - } + // return new ParseResult(new EventDef() + // { + // Index = index, + // Name = name, + // Inherited = inherited, + // ArgumentType = argType.Value, + // AutoDelivered = !subscribable, + // Annotations = annotations + // }, offset - oOffset); + //} - public byte[] Compose(EpConnection connection) - { - var name = Name.ToBytes(); + //public byte[] Compose(EpConnection connection) + //{ + // var name = Name.ToBytes(); - var hdr = Inherited ? (byte)0x80 : (byte)0; + // var hdr = Inherited ? (byte)0x80 : (byte)0; - if (Subscribable) - hdr |= 0x8; + // if (Subscribable) + // hdr |= 0x8; - if (Annotations != null) - { - var exp = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); //( DC.ToBytes(Annotation); - hdr |= 0x50; - return new BinaryList() - .AddUInt8(hdr) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ArgumentType.Compose(connection)) - .AddInt32(exp.Length) - .AddUInt8Array(exp) - .ToArray(); - } - else - hdr |= 0x40; + // if (Annotations != null) + // { + // var exp = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); //( DC.ToBytes(Annotation); + // hdr |= 0x50; + // return new BinaryList() + // .AddUInt8(hdr) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ArgumentType.Compose(connection)) + // .AddInt32(exp.Length) + // .AddUInt8Array(exp) + // .ToArray(); + // } + // else + // hdr |= 0x40; - return new BinaryList() - .AddUInt8(hdr) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ArgumentType.Compose(connection)) - .ToArray(); - } + // return new BinaryList() + // .AddUInt8(hdr) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ArgumentType.Compose(connection)) + // .ToArray(); + //} public static EventDef MakeEventDef(Warehouse warehouse, Type type, EventInfo ei, byte index, string name, TypeDef schema) @@ -124,7 +127,7 @@ public class EventDef : MemberDef throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`"); var annotationAttrs = ei.GetCustomAttributes(true); - var subscribableAttr = ei.GetCustomAttribute(true); + var autoDeliveredAttr = ei.GetCustomAttribute(true); //evtType.Nullable = new NullabilityInfoContext().Create(ei).ReadState is NullabilityState.Nullable; @@ -175,7 +178,7 @@ public class EventDef : MemberDef Inherited = ei.DeclaringType != type, Annotations = annotations, EventInfo = ei, - Subscribable = subscribableAttr != null + AutoDelivered = autoDeliveredAttr != null }; } diff --git a/Libraries/Esiur/Data/Types/EventDefField.cs b/Libraries/Esiur/Data/Types/EventDefField.cs new file mode 100644 index 0000000..5cb9f44 --- /dev/null +++ b/Libraries/Esiur/Data/Types/EventDefField.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum EventDefField : byte + { + ArgumentType = 0x03, // TypeRepresentation + ArgumentName = 0x04, // string + OrderingControl = 0x05, // OrderingControl + HistoryControl = 0x06, // HistoryControl + } +} diff --git a/Libraries/Esiur/Data/Types/EventDefFlags.cs b/Libraries/Esiur/Data/Types/EventDefFlags.cs new file mode 100644 index 0000000..ca37124 --- /dev/null +++ b/Libraries/Esiur/Data/Types/EventDefFlags.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum EventDefFlags : byte + { + None = 0x00, + Inherited = (byte)MemberDefFlags.Inherited, + Deprecated = (byte)MemberDefFlags.Deprecated, + + AutoDelivered = 0x04, // Delivered to attached clients without explicit subscription. + Historical = 0x08, + } +} diff --git a/Libraries/Esiur/Data/Types/ExampleField.cs b/Libraries/Esiur/Data/Types/ExampleField.cs new file mode 100644 index 0000000..c14a89f --- /dev/null +++ b/Libraries/Esiur/Data/Types/ExampleField.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum ExampleField : byte + { + Title = 0x00, // string + Description = 0x01, // string + Arguments = 0x02, // Map + Result = 0x03, // object + } +} diff --git a/Libraries/Esiur/Data/Types/FunctionDef.cs b/Libraries/Esiur/Data/Types/FunctionDef.cs index ea61bfe..ae2015d 100644 --- a/Libraries/Esiur/Data/Types/FunctionDef.cs +++ b/Libraries/Esiur/Data/Types/FunctionDef.cs @@ -30,6 +30,13 @@ public class FunctionDef : MemberDef public Tru ReturnType { get; set; } public bool IsStatic { get; set; } + public bool ReadOnly { get; set; } + public bool Idempotent { get; set; } + public bool Cancellable { get; set; } + public bool Deprecated { get; set; } + + //public FunctionDefFlags Flags { get; set; } + public StreamMode StreamMode { get; set; } public ArgumentDef[] Arguments { get; set; } diff --git a/Libraries/Esiur/Data/Types/FunctionDefField.cs b/Libraries/Esiur/Data/Types/FunctionDefField.cs new file mode 100644 index 0000000..c309637 --- /dev/null +++ b/Libraries/Esiur/Data/Types/FunctionDefField.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum FunctionDefField : byte + { + Arguments = 0x03, // List> + ReturnType = 0x04, // TRU + StreamMode = 0x05, // StreamMode + } +} diff --git a/Libraries/Esiur/Data/Types/FunctionDefFlags.cs b/Libraries/Esiur/Data/Types/FunctionDefFlags.cs new file mode 100644 index 0000000..20a57e7 --- /dev/null +++ b/Libraries/Esiur/Data/Types/FunctionDefFlags.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum FunctionDefFlags : byte + { + None = 0x00, + Inherited = (byte)MemberDefFlags.Inherited, + Deprecated = (byte)MemberDefFlags.Deprecated, + + Static = 0x04, + ReadOnly = 0x08, + Idempotent = 0x10, + Cancellable = 0x20, + } +} \ No newline at end of file diff --git a/Libraries/Esiur/Data/Types/LocalTypeDef.cs b/Libraries/Esiur/Data/Types/LocalTypeDef.cs index b65dace..663cfd0 100644 --- a/Libraries/Esiur/Data/Types/LocalTypeDef.cs +++ b/Libraries/Esiur/Data/Types/LocalTypeDef.cs @@ -361,56 +361,58 @@ public class LocalTypeDef:TypeDef } - public override byte[] Compose(EpConnection connection) - { - // bake it binarily - var b = new BinaryList(); + //public override byte[] Compose(EpConnection connection) + //{ + // // bake it binarily + // var b = new BinaryList(); - // find the first parent type that implements IResource + // // find the first parent type that implements IResource - var hasParent = ParentTypeDef != null; - var hasClassAnnotation = Annotations != null && Annotations.Count() > 0; + // var hasParent = ParentTypeDef != null; + // var hasClassAnnotation = Annotations != null && Annotations.Count() > 0; - var typeNameBytes = DC.ToBytes(_typeName); + // var typeNameBytes = DC.ToBytes(_typeName); - b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)_typeDefKind)) - .AddUInt64(_typeId) - .AddUInt8((byte)typeNameBytes.Length) - .AddUInt8Array(typeNameBytes); + // b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)_typeDefKind)) + // .AddUInt64(_typeId) + // .AddUInt8((byte)typeNameBytes.Length) + // .AddUInt8Array(typeNameBytes); - if (hasParent) - { - b.AddUInt64(ParentTypeDef.Id); - } + // if (hasParent) + // { + // b.AddUInt64(ParentTypeDef.Id); + // } - if (hasClassAnnotation) - { + // if (hasClassAnnotation) + // { - //foreach (var ann in Annotations) - // Annotations.Add(ann.Key, ann.Value); + // //foreach (var ann in Annotations) + // // Annotations.Add(ann.Key, ann.Value); - var classAnnotationBytes = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); + // var classAnnotationBytes = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); - b.AddUInt8Array(classAnnotationBytes); + // b.AddUInt8Array(classAnnotationBytes); - } + // } - b.AddInt32(_version) - .AddUInt16((ushort)(_functions.Count + _properties.Count + _events.Count + _constants.Count)); + // b.AddInt32(_version) + // .AddUInt16((ushort)(_functions.Count + _properties.Count + _events.Count + _constants.Count)); - foreach (var ft in _functions) - b.AddUInt8Array(ft.Compose(connection )); - foreach (var pt in _properties) - b.AddUInt8Array(pt.Compose(connection)); - foreach (var et in _events) - b.AddUInt8Array(et.Compose(connection)); - foreach (var ct in _constants) - b.AddUInt8Array(ct.Compose(connection)); + // foreach (var ft in _functions) + // b.AddUInt8Array(ft.Compose(connection )); + // foreach (var pt in _properties) + // b.AddUInt8Array(pt.Compose(connection)); + // foreach (var et in _events) + // b.AddUInt8Array(et.Compose(connection)); + // foreach (var ct in _constants) + // b.AddUInt8Array(ct.Compose(connection)); + + // return b.ToArray(); + + //} - return b.ToArray(); - } public static bool HasParent(Type type) { diff --git a/Libraries/Esiur/Data/Types/MemberDef.cs b/Libraries/Esiur/Data/Types/MemberDef.cs index 50ed7b1..a93daa2 100644 --- a/Libraries/Esiur/Data/Types/MemberDef.cs +++ b/Libraries/Esiur/Data/Types/MemberDef.cs @@ -9,13 +9,55 @@ namespace Esiur.Data.Types; public class MemberDef { + // Core fields public byte Index { get; set; } - public string Name { get; set; } - public bool Inherited { get; set; } - public TypeDef Definition { get; set; } - - public string Fullname => Definition.Name + "." + Name; + public string Name { get; set; } = string.Empty; + + public bool Inherited { get; set; } + + public TypeDef Definition { get; set; } = null!; + + // Human-readable metadata + public string? Description { get; set; } + + public string? Usage { get; set; } + + public List? Examples { get; set; } + + public List? Tags { get; set; } + + // Value constraints and representation + public string? Unit { get; set; } + + public object? Minimum { get; set; } + + public object? Maximum { get; set; } + + public List? AllowedValues { get; set; } + + public string? Pattern { get; set; } + + public string? Format { get; set; } + + // Operational semantics + public List? Preconditions { get; set; } + + public List? Postconditions { get; set; } + + public OperationEffects Effects { get; set; } + + public List? Warnings { get; set; } + + public List? RelatedMembers { get; set; } + + // Compatibility guidance + public string? DeprecationMessage { get; set; } + + public string Fullname => + Definition is null || string.IsNullOrEmpty(Definition.Name) + ? Name + : $"{Definition.Name}.{Name}"; } diff --git a/Libraries/Esiur/Data/Types/MemberDefField.cs b/Libraries/Esiur/Data/Types/MemberDefField.cs new file mode 100644 index 0000000..0fb550f --- /dev/null +++ b/Libraries/Esiur/Data/Types/MemberDefField.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum MemberDefField : byte + { + // Core member fields + Index = 0x00, // byte + Name = 0x01, // string + Flags = 0x02, // byte: member-specific flags enum + + // Human-readable metadata + Description = 0x20, // string + Usage = 0x21, // string + Examples = 0x22, // List + Tags = 0x23, // List + + // Value constraints and representation + Unit = 0x24, // string + Minimum = 0x25, // object: same type as the value + Maximum = 0x26, // object: same type as the value + AllowedValues = 0x27, // List + Pattern = 0x28, // string + Format = 0x29, // string + + // Operational semantics + Preconditions = 0x2A, // List + Postconditions = 0x2B, // List + Effects = 0x2C, // OperationEffects + Warnings = 0x2D, // List + RelatedMembers = 0x2E, // List: member indexes + + // Compatibility guidance + DeprecationMessage = 0x2F, // string + } +} diff --git a/Libraries/Esiur/Data/Types/MemberDefFlags.cs b/Libraries/Esiur/Data/Types/MemberDefFlags.cs new file mode 100644 index 0000000..4fd2cd2 --- /dev/null +++ b/Libraries/Esiur/Data/Types/MemberDefFlags.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum MemberDefFlags : byte + { + None = 0x00, + Inherited = 0x01, // Member is inherited from a parent TypeDef. + Deprecated = 0x02, // Member is retained for compatibility but should be avoided. + } +} diff --git a/Libraries/Esiur/Data/Types/OperationEffects.cs b/Libraries/Esiur/Data/Types/OperationEffects.cs new file mode 100644 index 0000000..cafd8a2 --- /dev/null +++ b/Libraries/Esiur/Data/Types/OperationEffects.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum OperationEffects : byte + { + None = 0x00, + + // Changes one or more properties of the target resource. + ModifiesState = 0x01, + + // Creates a resource or another persistent object. + CreatesResource = 0x02, + + // Deletes or disposes a resource. + DeletesResource = 0x04, + + // Produces externally visible output or communication. + External = 0x08, + + // Emits events or notifications. + EmitsEvents = 0x10, + + // Effects are not fully known or described. + Unknown = 0x80 + } +} diff --git a/Libraries/Esiur/Data/Types/PropertyDef.cs b/Libraries/Esiur/Data/Types/PropertyDef.cs index 9caba10..930277a 100644 --- a/Libraries/Esiur/Data/Types/PropertyDef.cs +++ b/Libraries/Esiur/Data/Types/PropertyDef.cs @@ -36,19 +36,18 @@ public class PropertyDef : MemberDef */ //bool ReadOnly; //EPTypes::DataType ReturnType; - public PropertyPermission Permission - { - get; - set; - } + //public PropertyPermission Permission + //{ + // get; + // set; + //} + public bool ReadOnly { get; set; } + + public bool Constant { get; set; } //public bool IsNullable { get; set; } - public bool HasHistory - { - get; - set; - } + public bool Historical { get; set; } /* public PropertyType Mode @@ -81,124 +80,124 @@ public class PropertyDef : MemberDef return $"{Name}: {ValueType}"; } - public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) - { - var oOffset = offset; + //public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) + //{ + // var oOffset = offset; - var hasAnnotation = ((data[offset] & 0x8) == 0x8); - var hasHistory = ((data[offset] & 1) == 1); - var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3); - var name = data.GetString(offset + 1, data[offset]); + // var hasAnnotation = ((data[offset] & 0x8) == 0x8); + // var hasHistory = ((data[offset] & 1) == 1); + // var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3); + // var name = data.GetString(offset + 1, data[offset]); - //Console.WriteLine("Parsing propdef " + name); + // //Console.WriteLine("Parsing propdef " + name); - offset += (uint)data[offset] + 1; + // offset += (uint)data[offset] + 1; - var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence); + // var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence); - offset += valueType.Size; + // offset += valueType.Size; - Map annotations = null; + // Map annotations = null; - // arguments - if (hasAnnotation) // Annotation ? - { - var (len, anns) = Codec.ParseSync(data, offset, null); + // // arguments + // if (hasAnnotation) // Annotation ? + // { + // var (len, anns) = Codec.ParseSync(data, offset, null); - if (anns is Map map) - annotations = map; + // if (anns is Map map) + // annotations = map; - offset += len; - } + // offset += len; + // } - return new ParseResult(new PropertyDef() - { - Index = index, - Name = name, - Inherited = inherited, - Permission = permission, - HasHistory = hasHistory, - ValueType = valueType.Value, - Annotations = annotations - }, offset - oOffset); + // return new ParseResult(new PropertyDef() + // { + // Index = index, + // Name = name, + // Inherited = inherited, + // Permission = permission, + // HasHistory = hasHistory, + // ValueType = valueType.Value, + // Annotations = annotations + // }, offset - oOffset); - } + //} - public byte[] Compose(EpConnection connection) - { - var name = DC.ToBytes(Name); + //public byte[] Compose(EpConnection connection) + //{ + // var name = DC.ToBytes(Name); - var pv = ((byte)(Permission) << 1) | (HasHistory ? 1 : 0); + // var pv = ((byte)(Permission) << 1) | (HasHistory ? 1 : 0); - if (Inherited) - pv |= 0x80; + // if (Inherited) + // pv |= 0x80; - //if (WriteAnnotation != null && ReadAnnotation != null) - //{ - // var rexp = DC.ToBytes(ReadAnnotation); - // var wexp = DC.ToBytes(WriteAnnotation); - // return new BinaryList() - // .AddUInt8((byte)(0x38 | pv)) - // .AddUInt8((byte)name.Length) - // .AddUInt8Array(name) - // .AddUInt8Array(ValueType.Compose()) - // .AddInt32(wexp.Length) - // .AddUInt8Array(wexp) - // .AddInt32(rexp.Length) - // .AddUInt8Array(rexp) - // .ToArray(); - //} - //else if (WriteAnnotation != null) - //{ - // var wexp = DC.ToBytes(WriteAnnotation); - // return new BinaryList() - // .AddUInt8((byte)(0x30 | pv)) - // .AddUInt8((byte)name.Length) - // .AddUInt8Array(name) - // .AddUInt8Array(ValueType.Compose()) - // .AddInt32(wexp.Length) - // .AddUInt8Array(wexp) - // .ToArray(); - //} - //else if (ReadAnnotation != null) - //{ - // var rexp = DC.ToBytes(ReadAnnotation); - // return new BinaryList() - // .AddUInt8((byte)(0x28 | pv)) - // .AddUInt8((byte)name.Length) - // .AddUInt8Array(name) - // .AddUInt8Array(ValueType.Compose()) - // .AddInt32(rexp.Length) - // .AddUInt8Array(rexp) - // .ToArray(); - //} - if (Annotations != null) - { - var rexp = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); - return new BinaryList() - .AddUInt8((byte)(0x28 | pv)) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose(connection)) - .AddUInt8Array(rexp) - .ToArray(); - } - else - { - return new BinaryList() - .AddUInt8((byte)(0x20 | pv)) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose(connection)) - .ToArray(); - } - } + // //if (WriteAnnotation != null && ReadAnnotation != null) + // //{ + // // var rexp = DC.ToBytes(ReadAnnotation); + // // var wexp = DC.ToBytes(WriteAnnotation); + // // return new BinaryList() + // // .AddUInt8((byte)(0x38 | pv)) + // // .AddUInt8((byte)name.Length) + // // .AddUInt8Array(name) + // // .AddUInt8Array(ValueType.Compose()) + // // .AddInt32(wexp.Length) + // // .AddUInt8Array(wexp) + // // .AddInt32(rexp.Length) + // // .AddUInt8Array(rexp) + // // .ToArray(); + // //} + // //else if (WriteAnnotation != null) + // //{ + // // var wexp = DC.ToBytes(WriteAnnotation); + // // return new BinaryList() + // // .AddUInt8((byte)(0x30 | pv)) + // // .AddUInt8((byte)name.Length) + // // .AddUInt8Array(name) + // // .AddUInt8Array(ValueType.Compose()) + // // .AddInt32(wexp.Length) + // // .AddUInt8Array(wexp) + // // .ToArray(); + // //} + // //else if (ReadAnnotation != null) + // //{ + // // var rexp = DC.ToBytes(ReadAnnotation); + // // return new BinaryList() + // // .AddUInt8((byte)(0x28 | pv)) + // // .AddUInt8((byte)name.Length) + // // .AddUInt8Array(name) + // // .AddUInt8Array(ValueType.Compose()) + // // .AddInt32(rexp.Length) + // // .AddUInt8Array(rexp) + // // .ToArray(); + // //} + // if (Annotations != null) + // { + // var rexp = Codec.Compose(Annotations, connection.Instance.Warehouse, connection); + // return new BinaryList() + // .AddUInt8((byte)(0x28 | pv)) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ValueType.Compose(connection)) + // .AddUInt8Array(rexp) + // .ToArray(); + // } + // else + // { + // return new BinaryList() + // .AddUInt8((byte)(0x20 | pv)) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ValueType.Compose(connection)) + // .ToArray(); + // } + //} - public static PropertyDef MakePropertyDef(Warehouse warehouse, Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema) + public static PropertyDef MakePropertyDef(Warehouse warehouse, Type type, PropertyInfo pi, string name, byte index, TypeDef typeDef) { var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null; // @TODO: need to check if the type is remote @@ -211,7 +210,7 @@ public class PropertyDef : MemberDef throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`"); var annotationAttrs = pi.GetCustomAttributes(true); - var storageAttr = pi.GetCustomAttribute(true); + var historicalAttr = pi.GetCustomAttribute(true); //var nullabilityContext = new NullabilityInfoContext(); //propType.Nullable = nullabilityContext.Create(pi).ReadState is NullabilityState.Nullable; @@ -267,8 +266,7 @@ public class PropertyDef : MemberDef Inherited = pi.DeclaringType != type, ValueType = propType, PropertyInfo = pi, - HasHistory = storageAttr == null ? false : storageAttr.Mode == StorageMode.History, - Permission = permission, + Historical = historicalAttr == null, Annotations = annotations, }; diff --git a/Libraries/Esiur/Data/Types/PropertyDefField.cs b/Libraries/Esiur/Data/Types/PropertyDefField.cs new file mode 100644 index 0000000..2329914 --- /dev/null +++ b/Libraries/Esiur/Data/Types/PropertyDefField.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum PropertyDefField : byte + { + ValueType = 0x03, // TypeRepresentation + OrderingControl = 0x04, // OrderingControl + HistoryControl = 0x05, // HistoryControl + DefaultValue = 0x06, // object + } +} diff --git a/Libraries/Esiur/Data/Types/PropertyDefFlags.cs b/Libraries/Esiur/Data/Types/PropertyDefFlags.cs new file mode 100644 index 0000000..8ec7e68 --- /dev/null +++ b/Libraries/Esiur/Data/Types/PropertyDefFlags.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum PropertyDefFlags : byte + { + None = 0x00, + Inherited = (byte)MemberDefFlags.Inherited, + Deprecated = (byte)MemberDefFlags.Deprecated, + + ReadOnly = 0x04, // Property cannot be changed by remote Set. + Constant = 0x08, // Property has a constant valu + Volatile = 0x10, // Value may change but is not necessarily synchronized. + Historical = 0x20, // Previous values are retained and may be fetched. + } +} diff --git a/Libraries/Esiur/Data/Types/StreamMode.cs b/Libraries/Esiur/Data/Types/StreamMode.cs new file mode 100644 index 0000000..71d14a6 --- /dev/null +++ b/Libraries/Esiur/Data/Types/StreamMode.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + [Flags] + public enum StreamMode:byte + { + None = 0, + Push = 0x1, // Stream is in push mode, where data is sent from the source to the destination. + Pull = 0x2, // Stream is in pull mode, where data is requested from the source by the destination. + } +} diff --git a/Libraries/Esiur/Data/Types/TypeDef.cs b/Libraries/Esiur/Data/Types/TypeDef.cs index cd72d03..fe54274 100644 --- a/Libraries/Esiur/Data/Types/TypeDef.cs +++ b/Libraries/Esiur/Data/Types/TypeDef.cs @@ -32,16 +32,7 @@ public class TypeDef protected int _version; protected TypeDefKind _typeDefKind; - //Type _definedType { get; set; } - //Type _proxyType { get; set; } - //Type _parentDefinedType { get; set; } - //bool _isLocal; - - //public Type DefinedType => _definedType; - //public Type ParentDefinedType => _parentDefinedType; - - //public Type ProxyType => _proxyType; public override string ToString() { @@ -53,13 +44,8 @@ public class TypeDef public ulong? ParentTypeId => _parentTypeId; - //public byte[] Content => _content; - public TypeDefKind Kind => _typeDefKind; - - - public EventDef GetEventDefByName(string eventName) { foreach (var i in _events) diff --git a/Libraries/Esiur/Data/Types/TypeDefField.cs b/Libraries/Esiur/Data/Types/TypeDefField.cs new file mode 100644 index 0000000..ba0cf2a --- /dev/null +++ b/Libraries/Esiur/Data/Types/TypeDefField.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data.Types +{ + public enum TypeDefField : byte + { + Version = 0x00, + Id = 0x01, + Name = 0x02, + Namespace = 0x03, + Kind = 0x04, + Parent = 0x05, + Properties = 0x06, + Functions = 0x07, + Events = 0x08, + Constants = 0x09, + + Usage = 0x20, + Description = 0x21, + Example = 0x22, + Category = 0x23, + Since = 0x24, + Annotations = 0x25, + } +} diff --git a/Libraries/Esiur/Resource/AnnotationAttribute.cs b/Libraries/Esiur/Resource/Attributes/AnnotationAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/AnnotationAttribute.cs rename to Libraries/Esiur/Resource/Attributes/AnnotationAttribute.cs diff --git a/Libraries/Esiur/Resource/AttributeAttribute.cs b/Libraries/Esiur/Resource/Attributes/AttributeAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/AttributeAttribute.cs rename to Libraries/Esiur/Resource/Attributes/AttributeAttribute.cs diff --git a/Libraries/Esiur/Resource/SubscribableAttribute.cs b/Libraries/Esiur/Resource/Attributes/AutoDeliveryAttribute.cs similarity index 79% rename from Libraries/Esiur/Resource/SubscribableAttribute.cs rename to Libraries/Esiur/Resource/Attributes/AutoDeliveryAttribute.cs index e39c935..26ccbf5 100644 --- a/Libraries/Esiur/Resource/SubscribableAttribute.cs +++ b/Libraries/Esiur/Resource/Attributes/AutoDeliveryAttribute.cs @@ -29,12 +29,15 @@ using System.Threading.Tasks; namespace Esiur.Resource; -[AttributeUsage(AttributeTargets.Event)] -public class SubscribableAttribute : System.Attribute +/// +/// Indicates that an event is delivered to attached clients without +/// requiring an explicit subscription. +/// +[AttributeUsage( + AttributeTargets.Event | + AttributeTargets.Field, + AllowMultiple = false, + Inherited = true)] +public sealed class AutoDeliveredAttribute : Attribute { - - public SubscribableAttribute() - { - - } -} +} \ No newline at end of file diff --git a/Libraries/Esiur/Resource/Attributes/CancellableAttribute.cs b/Libraries/Esiur/Resource/Attributes/CancellableAttribute.cs new file mode 100644 index 0000000..be957ff --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/CancellableAttribute.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Indicates that a running remote invocation may be cancelled. + /// + [AttributeUsage( + AttributeTargets.Method, + AllowMultiple = false, + Inherited = true)] + public sealed class CancellableAttribute : Attribute + { + } +} diff --git a/Libraries/Esiur/Resource/ExportAttribute.cs b/Libraries/Esiur/Resource/Attributes/ExportAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/ExportAttribute.cs rename to Libraries/Esiur/Resource/Attributes/ExportAttribute.cs diff --git a/Libraries/Esiur/Resource/Attributes/HistoricalAttribute.cs b/Libraries/Esiur/Resource/Attributes/HistoricalAttribute.cs new file mode 100644 index 0000000..663d92b --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/HistoricalAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Indicates that previous values of an exported property are retained + /// and may be fetched remotely. + /// + [AttributeUsage( + AttributeTargets.Property | + AttributeTargets.Field, + AllowMultiple = false, + Inherited = true)] + public sealed class HistoricalAttribute : Attribute + { + + } +} diff --git a/Libraries/Esiur/Resource/Attributes/IdempotentAttribute.cs b/Libraries/Esiur/Resource/Attributes/IdempotentAttribute.cs new file mode 100644 index 0000000..099a94b --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/IdempotentAttribute.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Indicates that repeating the same invocation should have the same + /// externally observable effect as invoking it once. + /// + [AttributeUsage( + AttributeTargets.Method, + AllowMultiple = false, + Inherited = true)] + public sealed class IdempotentAttribute : Attribute + { + } + +} diff --git a/Libraries/Esiur/Resource/IgnoreAttribute.cs b/Libraries/Esiur/Resource/Attributes/IgnoreAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/IgnoreAttribute.cs rename to Libraries/Esiur/Resource/Attributes/IgnoreAttribute.cs diff --git a/Libraries/Esiur/Resource/ImportAttribute.cs b/Libraries/Esiur/Resource/Attributes/ImportAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/ImportAttribute.cs rename to Libraries/Esiur/Resource/Attributes/ImportAttribute.cs diff --git a/Libraries/Esiur/Resource/Attributes/OrderingAttribute.cs b/Libraries/Esiur/Resource/Attributes/OrderingAttribute.cs new file mode 100644 index 0000000..cd2a3e0 --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/OrderingAttribute.cs @@ -0,0 +1,28 @@ +using Esiur.Data; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Specifies non-default notification ordering for a property or event. + /// Absence of this attribute means strict ordering. + /// + [AttributeUsage( + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Event, + AllowMultiple = false, + Inherited = true)] + public sealed class OrderingAttribute : Attribute + { + public OrderingControl Control { get; } + + public OrderingAttribute(OrderingControl control) + { + Control = control; + } + } + +} diff --git a/Libraries/Esiur/Resource/Attributes/ReadOnlyAttribute.cs b/Libraries/Esiur/Resource/Attributes/ReadOnlyAttribute.cs new file mode 100644 index 0000000..b793718 --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/ReadOnlyAttribute.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Indicates that an exported function does not change resource state, + /// or that an exported property cannot be changed remotely. + /// + [AttributeUsage( + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field, + AllowMultiple = false, + Inherited = true)] + public sealed class ReadOnlyAttribute : Attribute + { + + } +} diff --git a/Libraries/Esiur/Resource/RemoteAttribute.cs b/Libraries/Esiur/Resource/Attributes/RemoteAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/RemoteAttribute.cs rename to Libraries/Esiur/Resource/Attributes/RemoteAttribute.cs diff --git a/Libraries/Esiur/Resource/ResourceAttribute.cs b/Libraries/Esiur/Resource/Attributes/ResourceAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/ResourceAttribute.cs rename to Libraries/Esiur/Resource/Attributes/ResourceAttribute.cs diff --git a/Libraries/Esiur/Resource/StorageAttribute.cs b/Libraries/Esiur/Resource/Attributes/StorageAttribute.cs similarity index 100% rename from Libraries/Esiur/Resource/StorageAttribute.cs rename to Libraries/Esiur/Resource/Attributes/StorageAttribute.cs diff --git a/Libraries/Esiur/Resource/Attributes/StreamAttribute.cs b/Libraries/Esiur/Resource/Attributes/StreamAttribute.cs new file mode 100644 index 0000000..29e68dd --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/StreamAttribute.cs @@ -0,0 +1,34 @@ +using Esiur.Data.Types; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + /// + /// Marks an exported function as streaming and specifies its delivery mode. + /// + [AttributeUsage( + AttributeTargets.Method, + AllowMultiple = false, + Inherited = true)] + public sealed class StreamAttribute : Attribute + { + /// + /// Gets the stream delivery mode. + /// + public StreamMode Mode { get; } + + /// + /// Indicates whether a push stream may be paused and resumed remotely. + /// This should only be true when Mode is Push. + /// + public bool Pausable { get; set; } + + public StreamAttribute(StreamMode mode = StreamMode.Push) + { + Mode = mode; + } + } + +} diff --git a/Libraries/Esiur/Resource/Attributes/UsageAttribute.cs b/Libraries/Esiur/Resource/Attributes/UsageAttribute.cs new file mode 100644 index 0000000..063c247 --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/UsageAttribute.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource.Attributes +{ + [AttributeUsage( + AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Interface | + AttributeTargets.Enum | + AttributeTargets.Delegate | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Event | + AttributeTargets.Parameter | + AttributeTargets.ReturnValue, + AllowMultiple = false, + Inherited = true)] + public sealed class UsageAttribute : Attribute + { + public string Value { get; } + + public UsageAttribute(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException( + "Usage text cannot be null or empty.", + nameof(value)); + + Value = value; + } + } +} diff --git a/Libraries/Esiur/Resource/Attributes/VolatileAttribute.cs b/Libraries/Esiur/Resource/Attributes/VolatileAttribute.cs new file mode 100644 index 0000000..9386df4 --- /dev/null +++ b/Libraries/Esiur/Resource/Attributes/VolatileAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource.Attributes +{ + /// + /// Indicates that an exported property has volatile synchronization + /// semantics. This is unrelated to the C# volatile memory modifier. + /// + [AttributeUsage( + AttributeTargets.Property | + AttributeTargets.Field, + AllowMultiple = false, + Inherited = true)] + public sealed class VolatileAttribute : Attribute + { + + } +} diff --git a/Libraries/Esiur/Resource/PropertyPermission.cs b/Libraries/Esiur/Resource/PropertyPermission.cs index 3becb6f..2d72b06 100644 --- a/Libraries/Esiur/Resource/PropertyPermission.cs +++ b/Libraries/Esiur/Resource/PropertyPermission.cs @@ -4,10 +4,10 @@ using System.Text; namespace Esiur.Resource { - public enum PropertyPermission : byte - { - Read = 1, - Write, - ReadWrite, - } + //public enum PropertyPermission : byte + //{ + // ReadOnly = 0, + // Write, + // ReadWrite, + //} }