Skip to content

StarCruiseStudios/PhxLib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PhxLib

PHX Lib core utilities and extensions.

PHX.Collections

New collection types that have more convenient interfaces and covariant generics.

Interfaces

  • IPhxContainer - A collection that contains multiple elements.
  • IPhxCollection/IPhxMutableCollection - A collection of elements that can be queried.
  • IPhxList/IPhxMutableList - A collection whose elements are ordered.
  • IPhxCatalog - A collection whose elements are unique.
  • IPhxSet/IPhxMutableSet - A collection whose elements are unique and can be merged or compared to other collections.
  • IPhxMap/IPhxMutableMap - A collection of key value pairs.
  • IPhxMultiMap/IPhxMutableMultiMap - A collection of mappings from keys to one or more values.
  • IPhxKeyValuePair - A single key value pair.

Collection Types

  • ImmutablePhxList
  • ImmutablePhxSet
  • ImmutablePhxMap
  • PhxArrayList
  • PhxHashSet
  • PhxHashMap
  • PhxArrayListMultiMap
  • PhxKeyValuePair

Conversion and Creation Helpers

  • IEnumerableConversionExtensions - Methods for converting an existing IEnumerable to a Phx.Collections type.
  • IEnumerableCopyExtensions - Methods for copying an existing IEnumerable into a new Phx.Collections type.
  • PhxCollections - Methods for creating new Phx.Collections types.

PHX.Debug

Utilities that assist in the debugging of code.

IDebugDisplay

Provides an interface for a different string representation of an object intended for debugging rather than user display.

using System.Diagnostics;
using Phx.Debug;

// System.Diagnostics.DebuggerDisplay attribute controls what is displayed in
// the IDEs debugger view.
[DebuggerDisplay(DebugDisplay.DEBUGGER_DISPLAY_STRING)]
public class MyClass : IDebugDisplay {
    // ToDebugDisplay() method is also available to invoke in debug logging.
    public string ToDebugDisplay() {
        return "MyClass";
    }
    
    public override string ToString() {
        // Or provide a default implementation that is intended for user display.
        return ToDebugDisplay();
    }
}

PHX.Dev

Utilities that assist in development tasks and documentation.

ToDo

Functions that throws a NotImplementedException or InvalidOperationException with that describe the reason the code path is invalid. This is similar to throwing the NotImplementedException or InvalidOperationException directly, except it "returns" a value to work better with assignment statements and argument passing.

using Phx.Dev;
                
var myList = new List<string> {
    ToDo.NotImplementedYet<string>("This parameter need to be computed.")
};

int i = Random.Shared.Next();
switch (i) {
    case 0:
        ToDo.NotImplementedYet("This case needs to be handled.");
        break;
    default:
        ToDo.NotSupportedYet("We haven't figured out how to handle this yet.");
        break;
}

ToDo Attributes

Attributes that document code that needs to be implemented or issues that need to be resolved but that do not prevent execution. These attributes are similar to using comments except they are more easily searched and the compiler helps avoid typos and enforce the presence of descriptions.

using Phx.Dev;

[KnownIssue("This class doesn't work all the time.",
    workaround: "Use the NotBroken class instead.",
    link: "www.todo/12345")]
public class BrokenClass { } 

public class TooBigClass {
    [Refactor("This function does too much and can be broken up into component classes.")]
    public void BigFunction() { }
}

[ToDo("This class should optimize its database accesses.", link: "www.todo/67890")]
public class DatabaseClass { }

PHX.Lang

Utilities that add or extend language features.

ICheckedDisposable

Extends the IDisposable interface with a publicly readable boolean property that indicates if the instance has been disposed.

using Phx.Lang;

public class MyResource: ICheckedDisposable {
    public bool IsDisposed { get; private set; }

    public void Dispose() {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing) {
        if(!IsDisposed) {
            if(disposing) {
                // Dispose managed resources.
            }
            IsDisposed = true;
        }
    }
    
    ~MyResource() {
        Dispose(disposing: false);
    }
}

Late.Init

Provides a self documenting way to indicate a non-nullable value will be initialized after the constructor is invoked, but before its first access.

This should only be used if it is guaranteed that the value will be set before it is accessed (via late injection, separate initializer methods, etc).

This is equivalent to using null!, but is more clear about the developer's intentions.

using Phx.Lang;

string str = Late.Init<string>();

Optional

Optional can be used as a wrapper around a return value that communicates to the caller that a value may not be returned. It enforces that that case is handled, and provides built in helper methods to handle it.

using Phx.Lang;

private IOptional<string> GetNickName() {
    return Optional.OfNullable(nickName);
}

private void UseAnOptional() {
    var nickName = GetNickName().OrElse(() => StringUtils.EmptyString);
}

Helper methods include:

using Phx.Lang;
                
// Create an Optional from a value that may be null.
string? nickName = ReadNickName();
IOptional<string> optionalNickName = Optional.OfNullable(nickName);

// Use Optional.Of() or Optional.EMPTY to explicitly create an optional if it is
// not possible to use Optional.OfNullable().
IOptional<string> result;
try {
    var value = ReadValue();
    result = Optional.Of(value);
} catch (Exception) {
    result = Optional<string>.EMPTY;
}

// Use Optional.If() to create an optional from a boolean condition.
IOptional<int> optional = Optional.If(valueIsSet(), 10);
using Phx.Lang;

// Perform custom logic based on whether the optional is present
if (optional.IsPresent) {
    // Always check `IsPresent` first as `Value` will throw an exception if the 
    // optional is empty.
    var myValue = optional.Value;
}

if (optional.IsEmpty) {
    return;
}
using Phx.Lang;

// Functional methods to handle the presence or absence of an optional value.
optional.IfPresent(myValue => { /* ... */ });
optional.IfEmpty(() => { /* ... */ });
using Phx.Lang;

// Operate on or transform an optional value only if it is present.
IOptional<string> optStr = optional.Map<int, string>(intValue => Optional.Of(intValue.ToString()));
using Phx.Lang;

// Get default values or try alternatives if an optional is not present.
var v = optional.OrElse(() => 100);

var v2 = optional.OrTry(() => AnotherMethodThatReturnsOptional())
        .OrTry(() => ADifferentMethodThatReturnsOptional())
        .OrThrow(() => new InvalidOperationException("None of the optional values were present."));

Result

The Result class allows a method to return a value or indicate failure without throwing exceptions. This enforces that the error cases are handled, and allows enumerations of the different error states.

using Phx.Lang;

private IResult<string, DatabaseException> ReadDatabaseEntry() {
    return Result.Success<string, DatabaseException>("Entry");
}

private void UseAResult() {
    var entry = ReadDatabaseEntry().OrElse(() => "No Entry");
}

The following helper methods are provided:

using Phx.Lang;
                
// Create a result from a successful value or exception.
Result.Success(10);
Result.Failure(new Exception());
using Phx.Lang;

// Check if the result is successful.
var result = GetAValue();
if (result.IsSuccess) {
    var value = (result as Success<string, Exception>)!.Result;
}

if (result.IsFailure) {
    var error = (result as Failure<string, Exception>)!.Error;
}
using Phx.Lang;

// Check the result for a specific outcome.
if (result.Contains(it => it == "Hello")) { /* ... */ }

if (result.ContainsError(it => it is DatabaseException)) { /* ... */ }
using Phx.Lang;

// Operate on or transform a result if it is successful.
var stringResult = result.Map(value => value.ToString());

// Operate on or transform a result if it failed.
var newResult = result.MapError(ex => new ParseException(ex));
using Phx.Lang;

// Get default values or try alternatives if a result failed.
var valueWithAlternative = result.OrElse(() => "Alternative");
var requiredValue = result.OrThrow();

Try.All

Additional utilities for trying multiple actions that may throw exceptions. If any exceptions were thrown, they are aggregated into a single AggregateException that is thrown after all actions have been executed. This is not an asynchronous operation, all actions are executed sequentially.

using Phx.Lang;

try {
    Try.All(
        () => { DoFirstThing(); },
        () => { DoSecondThing(); },
        () => { DoThirdThing(); }
    );
} catch (AggregateException ex) {
    foreach (var exception in ex.InnerExceptions) {
        // ...
    }
}

IEnumerable<int> allElements = GetElements();

try {
    Try.All((element) => { 
        DoSomethingWithEachItem(element); 
    }, allElements);
} catch (AggregateException ex) {
    ex.Handle((innerException) => {
        log(innerException);
        return true;
    });
}

Unit

Unit is type that is equivalent to the instantiation of void. It is useful to document that a function will never return a value, or as a way to pass around function references whose generic parameters require a return type.

using Phx.Lang;
using static Phx.Lang.Unit;

public Result<Unit, Exception> FunctionWithSideEffect() {
    if (PerformSomeSideEffect()) {
        return Result.Success(UNIT);
    } else {
        return Result.Failure(new Exception());
    } 
}

public Unit AssertFail() {
    throw new Exception(); 
}

String Utils and Extensions

Various string utilities and extensions are also provided.

using Phx.Lang;
using static Phx.Lang.StringUtils;

// Constant string values.
public void FunctionWithDefault(string str = EmptyString) {
    // String.Empty is a readonly field and cannot be used as a default argument
    // or parameter in Attributes. EmptyString is a constant and can be used in
    // those cases.
}

public string GetStringValue(string? value) {
    return value?.ToString() ?? NullString;
}
using Phx.Lang;

// Extension methods for converting objects to string.
object? obj = null;
string str = obj.ToStringSafe();

troublesomeObject.ToDebugDisplayString();
using Phx.Lang;

// Utilities for inline string building.
var newName = BuildString(sb => {
    sb.Append("First");
    sb.Append("Last");
});
using Phx.Lang;

// Extension methods for manipulating and escaping strings.
var uppercase = "hello".StartUppercase();
var lowercase = "Hello".StartLowercase();
var implClassName = "IMyInterface".RemoveLeadingI();

var verbatimString = "\"Hello\"".EscapeVerbatimString();
var unescapedVerbatimString = verbatimString.UnescapeVerbatimString();

var quoteString = "\"Hello\"".EscapeStringQuotes();
var unescapedQuoteString = quoteString.UnescapeStringQuotes();
using Phx.Lang;

// String case conversions
var constantName = "someVariableName".FromCamelCase().ToCapsCase();

if (inputValue.FromPascalCase().IsValid) {
    // ...
}

// Conversions and validations support:
// * camelCase
// * CAPS_CASE
// * kebab-case
// * PascalCase
// * snake_case

Copyright (c) 2022 Star Cruise Studios LLC. All rights reserved.
Licensed under the Apache License, Version 2.0.
See http://www.apache.org/licenses/LICENSE-2.0 for full license information.

About

Core C# language and development extensions

Topics

Resources

License

Stars

Watchers

Forks