/// <summary>Try to apply a text operation.</summary> /// <param name="target">The target map to change.</param> /// <param name="operation">The text operation to apply.</param> /// <param name="error">An error indicating why applying the operation failed, if applicable.</param> /// <returns>Returns whether applying the operation succeeded.</returns> private bool TryApplyTextOperation(Map target, TextOperation operation, out string error) { var targetRoot = operation.GetTargetRoot(); switch (targetRoot) { case TextOperationTargetRoot.MapProperties: { // validate if (operation.Target.Length > 2) return this.Fail($"a '{TextOperationTargetRoot.MapProperties}' path must only have one other segment for the property name.", out error); // get key/value string key = operation.Target[1].Value; string value = target.Properties.TryGetValue(key, out PropertyValue property) ? property.ToString() : null; // apply target.Properties[key] = operation.Apply(value); } break; default: return this.Fail( targetRoot == null ? $"unknown path root '{operation.Target[0]}'." : $"path root '{targetRoot}' isn't valid for an {nameof(PatchType.EditMap)} patch", out error ); } error = null; return true; }
/// <summary>Try to apply a text operation.</summary> /// <param name="operation">The text operation to apply.</param> /// <param name="hasEntry">Get whether the collection has the given entry.</param> /// <param name="getEntry">Get an entry from the collection.</param> /// <param name="removeEntry">Remove an entry from the collection.</param> /// <param name="setEntry">Add or replace an entry in the collection.</param> /// <param name="error">An error indicating why applying the operation failed, if applicable.</param> /// <returns>Returns whether applying the operation succeeded.</returns> private bool TryApplyTextOperation <TKey, TValue>(TextOperation operation, Func <TKey, bool> hasEntry, Func <TKey, TValue> getEntry, Action <TKey> removeEntry, Action <TKey, TValue> setEntry, out string error) { var targetRoot = operation.GetTargetRoot(); switch (targetRoot) { case TextOperationTargetRoot.Entries: { // validate if (typeof(TValue) != typeof(string)) { return(this.Fail($"an '{TextOperationTargetRoot.Entries}' text operation can only be used for string entries. For data model entries, use '{TextOperationTargetRoot.Fields}' instead.", out error)); } if (operation.Target.Length > 2) { return(this.Fail($"an '{TextOperationTargetRoot.Entries}' path must only have one other segment for the property name.", out error)); } // get key string rawKey = operation.Target[1].Value; TKey key; if (typeof(TKey) == typeof(string)) { key = (TKey)(object)rawKey; } else if (typeof(TKey) == typeof(int)) { if (!int.TryParse(rawKey, out int intKey)) { return(this.Fail($"can't use value '{rawKey}' as an entry key because this asset uses numeric keys.", out error)); } key = (TKey)(object)intKey; } else { return(this.Fail($"unsupported asset key type '{typeof(TKey).FullName}'.", out error)); } // get value string value = hasEntry(key) ? (string)(object)getEntry(key) : null; // set value setEntry(key, (TValue)(object)operation.Apply(value)); } break; case TextOperationTargetRoot.Fields: throw new NotImplementedException("TODO"); default: return(this.Fail( targetRoot == null ? $"unknown path root '{operation.Target[0]}'." : $"path root '{targetRoot}' isn't valid for an {nameof(PatchType.EditMap)} patch", out error )); } error = null; return(true); }