/// <inhertidoc />
        public override OperationLog[] Operate(PathString source, PathString destination, string key)
        {
            List <OperationLog> logs = new List <OperationLog>();

            PathString validDest = destination.Extension == "asset" ? destination : destination.ReplaceExtension("asset");

            // Try open file
            if (!source.TryAsFileStream(FileMode.Open, out FileStream stream))
            {
                return(Error($"Failed to get as stream"));
            }

            // Try to deserialize the yaml file
            GameData gd;

            using (stream)
            {
                using (TextReader r = new StreamReader(stream))
                {
                    if (!DeserializeGameDataYaml(r, out gd))
                    {
                        return(Error($"Failed to deserialize"));
                    }
                }
            }

            // Load the scriptable object
            ScriptableObject inst = default;

            try
            {
                inst = UTScriptableObject.LoadOrCreateSoAsset(validDest, key);
            }
            catch (Exception)
            {
                return(Error($"Failed to create a scriptable object of type {key}"));
            }

            AGameDataSo gdso = inst as AGameDataSo;

            if (gdso == null)
            {
                return(Error($"Provided key '{key}' does not derive from AGameDataSo"));
            }

            GenericPopulate(gd, ref gdso);
            logs.Add(new OperationLog()
            {
                Name = cOperationName, Category = EOperationLogCategory.SUCC, Description = "CovertToSo succeeded"
            });

            return(logs.ToArray());
        }
        /// <summary>
        /// Uses reflection to generically populate a ScritableObject that inherits AGameDataSo. It is expected that
        /// the keys in Properties and Collections correspond to fields on the given ScritableObject Type. This function
        /// looks up the field and, if it exists, analyzes the type. If the type is a string the value is added directly. If
        /// it is a primative, a parse is performed. If the type is an enum, then a parse is performed.
        /// If the field is AGameDataSo reference, then given string is interpreted as a key
        /// and the references array is searched for the reference. In any case, failure or error leads to skipping
        /// populating the field.
        /// </summary>
        /// <param name="populate"></param>
        /// <param name="references"></param>
        /// <param name="gamedataAssembly"></param>
        public OperationLog[] GenericPopulate(GameData data, ref AGameDataSo populate)
        {
            List <OperationLog> logs = new List <OperationLog>();

            AGameDataSo[] references = UTScriptableObject.GetAllInstances <AGameDataSo>();

            // Get the type and it's fields
            Type t = populate.GetType();

            FieldInfo[] fields = t.GetFields();

            // populate the key
            if (string.IsNullOrEmpty(data.Key))
            {
                logs.AddRange(Warning("Key field of GameData is not populated"));
            }
            populate.Key = data.Key;

            if (data.Properties != null)
            {
                // populate the properties
                foreach (KeyValuePair <string, string> kv in data.Properties)
                {
                    // get the field with the same key
                    FieldInfo f = fields.FirstOrDefault(e => e.Name == kv.Key);
                    if (f == default)
                    {
                        logs.AddRange(Warning($"key:{data.Key} So does not contain field {kv.Key}"));
                        continue;
                    }

                    // if the field exists, populate it based on it's type
                    if (f.FieldType == typeof(string))
                    {
                        f.SetValue(populate, kv.Value);
                    }
                    if (f.FieldType.IsPrimitive)
                    {
                        logs.AddRange(HandlePrimative(f, populate, kv.Value));                         // do convert
                    }
                    else if (UTType.GetTest(UTType.ETypeTest.ENUM)(f.FieldType))
                    {
                        logs.AddRange(HandleEnum(f, populate, kv.Value));                                                          // do enum
                    }
                    else if (f.FieldType.IsSubclassOf(typeof(AGameDataSo)))
                    {
                        logs.AddRange(HandleSo(f, populate, kv.Value, references));                                                    // do a refernece handle
                    }
                }
            }

            if (data.Collections != null)
            {
                // populate the collections
                foreach (KeyValuePair <string, string[]> kv in data.Collections)
                {
                    // get the field with the same key
                    FieldInfo f = fields.FirstOrDefault(e => e.Name == kv.Key);
                    if (f == default)
                    {
                        continue;
                    }

                    Type  elType = f.FieldType.GetElementType();
                    Array a      = Array.CreateInstance(elType, kv.Value.Length);

                    // if the field exists, populate it based on it's type
                    if (elType == typeof(string))
                    {
                        f.SetValue(populate, kv.Value);
                    }
                    if (elType.IsPrimitive)
                    {
                        logs.AddRange(HandlePrimativeArray(f, populate, kv.Value, a));                    // do convert
                    }
                    else if (UTType.GetTest(UTType.ETypeTest.ENUM)(elType))
                    {
                        logs.AddRange(HandleEnumArray(f, populate, kv.Value, a));                                                     // do enum
                    }
                    else if (elType.IsSubclassOf(typeof(AGameDataSo)))
                    {
                        logs.AddRange(HandleSoArray(f, populate, kv.Value, references, a));                                               // do a refernece handle
                    }
                }
            }

            return(logs.ToArray());

            // Singles
            OperationLog[] HandleSingle(FieldInfo fi, AGameDataSo obj, string value, Func <Type, string, object> converter)
            {
                try
                {
                    fi.SetValue(obj, converter(fi.FieldType, value));
                }
                catch (Exception)
                {
                    return(Error($"Failed to set {fi.Name} in key:{obj.Key} of value:{value}"));
                }

                return(new OperationLog[0]);
            }

            OperationLog[] HandlePrimative(FieldInfo fi, AGameDataSo obj, string value)
            => HandleSingle(fi, obj, value, (ty, v) => Convert.ChangeType(v, ty));

            OperationLog[] HandleEnum(FieldInfo fi, AGameDataSo obj, string value)
            => HandleSingle(fi, obj, value, (ty, v) => Enum.Parse(ty, v, true));


            // Array
            OperationLog[] HandleArray(FieldInfo fi, AGameDataSo obj, string[] value, Array pop, Func <Type, string, object> converter)
            {
                List <OperationLog> innerLogs = new List <OperationLog>();

                for (int i = 0; i < pop.Length; i++)
                {
                    try
                    {
                        pop.SetValue(converter(fi.FieldType, value[i]), i);
                    }
                    catch (Exception) {
                        innerLogs.AddRange(Error($"Failed to set {fi.Name} in key:{obj.Key} of value:{value}"));
                    }
                }

                fi.SetValue(obj, pop);
                return(logs.ToArray());
            }

            OperationLog[] HandlePrimativeArray(FieldInfo fi, AGameDataSo obj, string[] value, Array pop)
            => HandleArray(fi, obj, value, pop, (ty, v) => Convert.ChangeType(v, ty));

            OperationLog[] HandleEnumArray(FieldInfo fi, AGameDataSo obj, string[] value, Array pop)
            => HandleArray(fi, obj, value, pop, (ty, v) => Enum.Parse(ty, v, true));

            // Set an so generically
            OperationLog[] HandleSo(FieldInfo fi, AGameDataSo obj, string value, AGameDataSo[] refs)
            {
                AGameDataSo soRef = refs.FirstOrDefault(e => e.Key == value);

                if (soRef != default)
                {
                    fi.SetValue(obj, soRef);
                    return(Error($"Failed to set {fi.Name} in key:{obj.Key} of value:{value}"));
                }

                return(new OperationLog[0]);
            }

            // set an so array
            OperationLog[] HandleSoArray(FieldInfo fi, AGameDataSo obj, string[] value, AGameDataSo[] refs, Array pop)
            {
                List <OperationLog> innerLogs = new List <OperationLog>();

                for (int i = 0; i < pop.Length; i++)
                {
                    try
                    {
                        pop.SetValue(refs.FirstOrDefault(e => e.Key == value[i]), i);
                    }
                    catch (Exception)
                    {
                        innerLogs.AddRange(Error($"Failed to set {fi.Name} in key:{obj.Key} of value:{value}"));
                    }
                }

                fi.SetValue(obj, pop);
                return(logs.ToArray());
            }
        }