Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.0.0] Unreleased
## [1.0.1] UnReleased

- Fixing issue with powershell 5.1 compiling the c# code.

## [1.0.0] Released

- Initial release
2 changes: 1 addition & 1 deletion PSFluentObjectValidation/PSFluentObjectValidation.psd1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@{
RootModule = 'PSFluentObjectValidation.psm1'
ModuleVersion = '1.0.0'
ModuleVersion = '1.0.1'
GUID = '90ac3c83-3bd9-4da5-8705-7b82b21963c8'
Author = 'Joshua Wilson'
CompanyName = 'PwshDevs'
Expand Down
144 changes: 94 additions & 50 deletions PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ public static class PSFluentObjectValidation
{
// First get the property (should be an array)
if (!HasProperty(currentObject, propertyName))
throw new InvalidOperationException($"Property '{propertyName}' does not exist");
throw new InvalidOperationException(String.Format("Property '{0}' does not exist", propertyName));

object arrayObject = GetProperty(currentObject, propertyName);

if (arrayObject == null)
throw new InvalidOperationException($"Property '{propertyName}' is null");
throw new InvalidOperationException(String.Format("Property '{0}' is null", propertyName));

if (!IsArrayLike(arrayObject))
throw new InvalidOperationException($"Property '{propertyName}' is not an array");
throw new InvalidOperationException(String.Format("Property '{0}' is not an array", propertyName));

// Handle wildcard [*] - means all elements must exist and be valid
if (indexStr == "*")
Expand All @@ -87,20 +87,21 @@ public static class PSFluentObjectValidation
}

// Handle numerical index [0], [1], etc.
if (int.TryParse(indexStr, out int index))
int index;
if (int.TryParse(indexStr, out index))
{
return ProcessNumericalAccess(arrayObject, propertyName, index);
}

throw new InvalidOperationException($"Invalid array index '{indexStr}' for property '{propertyName}'");
throw new InvalidOperationException(String.Format("Invalid array index '{0}' for property '{1}'", indexStr, propertyName));
}

private static object ProcessWildcardAccess(object arrayObject, string propertyName)
{
int count = GetCount(arrayObject);

if (count == 0)
throw new InvalidOperationException($"Array '{propertyName}' is empty - cannot validate [*]");
throw new InvalidOperationException(String.Format("Array '{0}' is empty - cannot validate [*]", propertyName));

// For wildcard, we return the array object itself
// The next part in the chain will validate against all elements
Expand All @@ -112,18 +113,25 @@ public static class PSFluentObjectValidation
int count = GetCount(arrayObject);

if (index < 0 || index >= count)
throw new InvalidOperationException($"Array index [{index}] is out of bounds for '{propertyName}' (length: {count})");
throw new InvalidOperationException(String.Format("Array index [{0}] is out of bounds for '{1}' (length: {2})", index, propertyName, count));

// Get the specific element
if (arrayObject is Array array)
if (arrayObject is Array)
{
Array array = (Array)arrayObject;
return array.GetValue(index);
}

if (arrayObject is IList list)
if (arrayObject is IList)
{
IList list = (IList)arrayObject;
return list[index];
}

// For IEnumerable, we need to iterate to the index
if (arrayObject is IEnumerable enumerable)
if (arrayObject is IEnumerable)
{
IEnumerable enumerable = (IEnumerable)arrayObject;
int currentIndex = 0;
foreach (object item in enumerable)
{
Expand All @@ -133,22 +141,23 @@ public static class PSFluentObjectValidation
}
}

throw new InvalidOperationException($"Cannot access index [{index}] on array '{propertyName}'");
throw new InvalidOperationException(String.Format("Cannot access index [{0}] on array '{1}'", index, propertyName));
}

private static object ProcessPropertyWithValidation(object currentObject, string propertyName, char validator)
{
// Handle wildcard array wrapper first
if (currentObject is WildcardArrayWrapper wrapper)
if (currentObject is WildcardArrayWrapper)
{
WildcardArrayWrapper wrapper = (WildcardArrayWrapper)currentObject;
return ProcessWildcardPropertyAccess(wrapper.ArrayObject, propertyName + validator);
}

if (validator == '?')
{
// Handle object/array validation: key? - ONLY check existence, allow null values
if (!HasProperty(currentObject, propertyName))
throw new InvalidOperationException($"Property '{propertyName}' does not exist");
throw new InvalidOperationException(String.Format("Property '{0}' does not exist", propertyName));

object value = GetProperty(currentObject, propertyName);

Expand All @@ -158,7 +167,7 @@ public static class PSFluentObjectValidation
{
// For arrays, check that it's not empty
if (GetCount(value) == 0)
throw new InvalidOperationException($"Array '{propertyName}' is empty");
throw new InvalidOperationException(String.Format("Array '{0}' is empty", propertyName));
}
// Note: We don't check IsObjectLike here because ? only validates existence

Expand All @@ -168,27 +177,28 @@ public static class PSFluentObjectValidation
{
// Validate non-empty: key!
if (!HasProperty(currentObject, propertyName))
throw new InvalidOperationException($"Property '{propertyName}' does not exist");
throw new InvalidOperationException(String.Format("Property '{0}' does not exist", propertyName));

object value = GetProperty(currentObject, propertyName);

if (value == null)
throw new InvalidOperationException($"Property '{propertyName}' is null");
throw new InvalidOperationException(String.Format("Property '{0}' is null", propertyName));

if (IsEmpty(value))
throw new InvalidOperationException($"Property '{propertyName}' is empty or whitespace");
throw new InvalidOperationException(String.Format("Property '{0}' is empty or whitespace", propertyName));

return value;
}

throw new InvalidOperationException($"Unknown validator '{validator}'");
throw new InvalidOperationException(String.Format("Unknown validator '{0}'", validator));
}

private static object ProcessRegularProperty(object currentObject, string propertyName)
{
// Handle wildcard array wrapper - validate property exists on ALL elements
if (currentObject is WildcardArrayWrapper wrapper)
if (currentObject is WildcardArrayWrapper)
{
WildcardArrayWrapper wrapper = (WildcardArrayWrapper)currentObject;
return ProcessWildcardPropertyAccess(wrapper.ArrayObject, propertyName);
}

Expand All @@ -204,7 +214,7 @@ public static class PSFluentObjectValidation

// Regular property navigation - allow null values (only ! operator should reject nulls)
if (!HasProperty(currentObject, propertyName))
throw new InvalidOperationException($"Property '{propertyName}' does not exist");
throw new InvalidOperationException(String.Format("Property '{0}' does not exist", propertyName));

object value = GetProperty(currentObject, propertyName);

Expand All @@ -227,72 +237,74 @@ public static class PSFluentObjectValidation
}

// Validate ALL elements have this property
if (arrayObject is Array array)
if (arrayObject is Array)
{
Array array = (Array)arrayObject;
for (int i = 0; i < array.Length; i++)
{
object element = array.GetValue(i);
if (element == null)
throw new InvalidOperationException($"Array element [{i}] is null");
throw new InvalidOperationException(String.Format("Array element [{0}] is null", i));
if (!HasProperty(element, actualPropertyName))
throw new InvalidOperationException($"Array element [{i}] does not have property '{actualPropertyName}'");
throw new InvalidOperationException(String.Format("Array element [{0}] does not have property '{1}'", i, actualPropertyName));

if (validator.HasValue)
{
object elementValue = GetProperty(element, actualPropertyName);
if (elementValue == null)
throw new InvalidOperationException($"Property '{actualPropertyName}' in element [{i}] is null");
throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is null", actualPropertyName, i));
if (validator == '!' && IsEmpty(elementValue))
throw new InvalidOperationException($"Property '{actualPropertyName}' in element [{i}] is empty");
throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is empty", actualPropertyName, i));
}
}
return GetProperty(array.GetValue(0), actualPropertyName);
}

if (arrayObject is IList list)
if (arrayObject is IList)
{
IList list = (IList)arrayObject;
for (int i = 0; i < list.Count; i++)
{
object element = list[i];
if (element == null)
throw new InvalidOperationException($"Array element [{i}] is null");
throw new InvalidOperationException(String.Format("Array element [{0}] is null", i));
if (!HasProperty(element, actualPropertyName))
throw new InvalidOperationException($"Array element [{i}] does not have property '{actualPropertyName}'");
throw new InvalidOperationException(String.Format("Array element [{0}] does not have property '{1}'", i, actualPropertyName));

if (validator.HasValue)
{
object elementValue = GetProperty(element, actualPropertyName);
if (elementValue == null)
throw new InvalidOperationException($"Property '{actualPropertyName}' in element [{i}] is null");
throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is null", actualPropertyName, i));
if (validator == '!' && IsEmpty(elementValue))
throw new InvalidOperationException($"Property '{actualPropertyName}' in element [{i}] is empty");
throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is empty", actualPropertyName, i));
}
}
return GetProperty(list[0], actualPropertyName);
}

throw new InvalidOperationException($"Cannot validate wildcard array");
throw new InvalidOperationException(String.Format("Cannot validate wildcard array"));
}

private static void ValidatePropertyValue(object value, string propertyName, char validator, int? arrayIndex = null)
{
string context = arrayIndex.HasValue ? $" in array element [{arrayIndex}]" : "";
string context = arrayIndex.HasValue ? String.Format(" in array element [{0}]", arrayIndex) : "";

if (validator == '?')
{
if (value == null)
throw new InvalidOperationException($"Property '{propertyName}'{context} is null");
throw new InvalidOperationException(String.Format("Property '{0}'{1} is null", propertyName, context));

if (IsArrayLike(value) && GetCount(value) == 0)
throw new InvalidOperationException($"Array '{propertyName}'{context} is empty");
throw new InvalidOperationException(String.Format("Array '{0}'{1} is empty", propertyName, context));
}
else if (validator == '!')
{
if (value == null)
throw new InvalidOperationException($"Property '{propertyName}'{context} is null");
throw new InvalidOperationException(String.Format("Property '{0}'{1} is null", propertyName, context));

if (IsEmpty(value))
throw new InvalidOperationException($"Property '{propertyName}'{context} is empty or whitespace");
throw new InvalidOperationException(String.Format("Property '{0}'{1} is empty or whitespace", propertyName, context));
}
}

Expand All @@ -301,14 +313,23 @@ public static class PSFluentObjectValidation
{
if (obj == null) return false;

if (obj is Hashtable hashtable)
if (obj is Hashtable)
{
Hashtable hashtable = (Hashtable)obj;
return hashtable.ContainsKey(propertyName);
}

if (obj is IDictionary dictionary)
if (obj is IDictionary)
{
IDictionary dictionary = (IDictionary)obj;
return dictionary.Contains(propertyName);
}

if (obj is PSObject psObj)
if (obj is PSObject)
{
PSObject psObj = (PSObject)obj;
return psObj.Properties[propertyName] != null;
}

var type = obj.GetType();
return type.GetProperty(propertyName) != null || type.GetField(propertyName) != null;
Expand All @@ -318,16 +339,23 @@ public static class PSFluentObjectValidation
{
if (obj == null) return null;

if (obj is Hashtable hashtable)
if (obj is Hashtable)
{
Hashtable hashtable = (Hashtable)obj;
return hashtable[propertyName];
}

if (obj is IDictionary dictionary)
if (obj is IDictionary)
{
IDictionary dictionary = (IDictionary)obj;
return dictionary[propertyName];
}

if (obj is PSObject psObj)
if (obj is PSObject)
{
PSObject psObj = (PSObject)obj;
var prop = psObj.Properties[propertyName];
return prop?.Value;
return prop != null ? prop.Value : null;
}

var type = obj.GetType();
Expand Down Expand Up @@ -359,14 +387,21 @@ public static class PSFluentObjectValidation
{
if (obj == null) return 0;

if (obj is Array array)
if (obj is Array)
{
Array array = (Array)obj;
return array.Length;
}

if (obj is ICollection collection)
if (obj is ICollection)
{
ICollection collection = (ICollection)obj;
return collection.Count;
}

if (obj is IEnumerable enumerable)
if (obj is IEnumerable)
{
IEnumerable enumerable = (IEnumerable)obj;
int count = 0;
foreach (var item in enumerable)
count++;
Expand All @@ -380,14 +415,23 @@ public static class PSFluentObjectValidation
{
if (value == null) return true;

if (value is string str)
if (value is string)
{
string str = (string)value;
return string.IsNullOrWhiteSpace(str);
}

if (value is Array array)
if (value is Array)
{
Array array = (Array)value;
return array.Length == 0;
}

if (value is ICollection collection)
if (value is ICollection)
{
ICollection collection = (ICollection)value;
return collection.Count == 0;
}

return value.Equals("");
}
Expand All @@ -396,7 +440,7 @@ public static class PSFluentObjectValidation
// Wrapper class to handle wildcard array processing
public class WildcardArrayWrapper
{
public object ArrayObject { get; }
public object ArrayObject { get; set; }

public WildcardArrayWrapper(object arrayObject)
{
Expand Down
Loading