using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using lib; namespace imm; /* T O D O : T O D O : T O D O : x) Add unit tests for all this. This will definitely benefit from them */ static public class Util { //This can handle both Timed and Recorded static public T Process( ref T obj, Func fn, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0, [CallerArgumentExpression("fn")] string dbgExp = default ) where T : Recorded { obj = obj.Process( fn, reason, dbgName, dbgPath, lineNumber, dbgExp ); return obj; } static public T LightProcess( ref T obj, Func fn, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0, [CallerArgumentExpression("fn")] string dbgExp = default ) where T : Versioned { obj = obj.Process( fn, reason ); return obj; } } //[lib.Ser( Types = lib.Types.None )] public record class Versioned where T : Versioned { public delegate void ChangeDelegate( T ZZOld, T next ); public record class MetaData { public uint Version { get; internal set; } = 0; public string Reason { get; internal set; } = ""; public MetaData() { } } internal Versioned( MetaData meta ) { MetaStorage = meta; } internal MetaData MetaStorage = new(); public MetaData Meta => MetaStorage; [lib.Dont] public ChangeDelegate OnChange = (x, y) => {}; public T Process( Func fn, string reason = "" ) { var newT = fn( ( T )this ); return newT with { MetaStorage = Meta with { Version = newT.Meta.Version + 1, Reason = reason, } }; } } //[lib.Ser( Types = lib.Types.None )] public record class Recorded : Versioned where T : Recorded { new public record class MetaData : Versioned.MetaData { [lib.Dont] public T? ZZOld { get; internal set; } public T? Old => ZZOld; public string Expression { get; internal set; } = ""; public string MemberName { get; internal set; } = ""; public string FilePath { get; internal set; } = ""; public int LineNumber { get; internal set; } = -1; public MetaData() { } } public Recorded() : this( new MetaData() ) { } public Recorded(MetaData meta) : base( meta ) { } new public MetaData Meta => MetaStorage as MetaData; virtual public T Record( string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0 ) { return Process( t => t, reason, dbgName, dbgPath, lineNumber ); } virtual public T Process( T next, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0, [CallerArgumentExpression("next")] string dbgExp = default ) { return Process( ( old ) => next, reason, dbgName, dbgPath, lineNumber, dbgExp ); } virtual public T Process( Func fn, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0, [CallerArgumentExpression("fn")] string dbgExp = default ) { var orig = ( T )this; var next = fn( orig ); var ret = next with { //Do the Versioned code here MetaStorage = Meta with { Version = orig.Meta.Version + 1, Reason = reason, ZZOld = orig, MemberName = dbgName, FilePath = dbgPath, LineNumber = lineNumber, } }; OnChange( orig, ret ); return ret; } } public record class Timed : Recorded where T : Timed { new public record class MetaData : Recorded.MetaData { public readonly DateTime CreatedAt = DateTime.Now; public DateTime TouchedAt { get; internal set; } = DateTime.Now; } public Timed() : this( new MetaData() ) { } public Timed( MetaData meta ) : base( meta ) { } new public MetaData Meta => MetaStorage as MetaData; public TimeSpan Since => Meta.TouchedAt - Meta.Old?.Meta.TouchedAt ?? TimeSpan.MaxValue; override public T Record( string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0 ) { return Process( t => t, reason, dbgName, dbgPath, lineNumber ); } override public T Process( T next, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int lineNumber = 0, [CallerArgumentExpression("next")] string dbgExp = default ) { return Process( ( old ) => next, reason, dbgName, dbgPath, lineNumber, dbgExp ); } override public T Process( Func fn, string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = 0, [CallerArgumentExpression("fn")] string dbgExp = default ) { var orig = ( T )this; var next = fn( orig ); var ret = next with { MetaStorage = Meta with { //Versioned Version = orig.Meta.Version + 1, Reason = reason, //Recorded MemberName = dbgName, FilePath = dbgPath, LineNumber = dbgLine, ZZOld = orig, //Timed TouchedAt = DateTime.Now, } }; if( OnChange != null) OnChange( orig, ret ); return ret; } }