SAToolKit 1.0.0

dotnet add package SAToolKit --version 1.0.0
                    
NuGet\Install-Package SAToolKit -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SAToolKit" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SAToolKit" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="SAToolKit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SAToolKit --version 1.0.0
                    
#r "nuget: SAToolKit, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SAToolKit@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SAToolKit&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=SAToolKit&version=1.0.0
                    
Install as a Cake Tool

SAToolKit

South African developer helpers for .NET 10. ID validation, Eskom load shedding utilities, security scanning, async helpers, and collection tools — all named after things that make life in the Republic uniquely interesting.

NuGet License: MIT

Installation

dotnet add package SAToolKit

Modules

IdController — SA ID Number Validation

Validates South African ID numbers using the Luhn algorithm and extracts encoded metadata.

using SAToolKit.Controllers;

var id = new IdController();

id.IsValid("9003155001089");        // true
id.GetAge("9003155001089");         // int (age in years)
id.GetGender("9003155001089");      // SaGender.Male
id.GetDateOfBirth("9003155001089"); // DateTime
id.IsCitizen("9003155001089");      // true

SaIdResult profile = id.Validate("9003155001089");
// profile.IsValid, profile.Age, profile.Gender, profile.DateOfBirth,
// profile.Citizenship, profile.ErrorMessage

String extensions — chain directly off any string:

using SAToolKit.Extensions;

"9003155001089".IsValidSouthAfricanId();   // true
"9003155001089".GetAgeFromId();            // int?
"9003155001089".GetGenderFromId();         // SaGender?
"9003155001089".GetDateOfBirthFromId();    // DateTime?
"9003155001089".IsSouthAfricanCitizen();   // bool
"9003155001089".GetFullIdProfile();        // SaIdResult

EskomController — Load Shedding Utilities

Because Eskom needs its own controller.

using SAToolKit.Controllers;

var eskom = new EskomController();

eskom.WhatsTheStage();       // LoadSheddingResult (current stage info)
eskom.EskomRemembered();     // true when power is on
eskom.CookingByFlashlight(); // true when load shedding is active
eskom.WhatEskomSays();       // a classic Eskom quote for the current stage
eskom.SimulateStage(LoadSheddingStage.Stage4); // test against a specific stage

Swap in your own ILoadSheddingService to connect live stage data from an API.


Braai — Value Comparison

Inspired by the eternal debate: KFC vs Hungry Lion.

using SAToolKit.Controllers;

// Pick the better deal
var winner = Braai.BestValue(dealA, dealB, x => x.ValuePerRand);

// Is this a Hungry Lion deal? (value/price >= threshold)
Braai.IsHungryLionDeal(price: 89, value: 150);           // true
Braai.IsHungryLionDeal(price: 89, value: 150, threshold: 2.0); // false

// Rank all options, best first
var ranked = Braai.RankByValue(deals, x => x.ValuePerRand);

// Take the single best option from a collection
var best = Braai.TakeTheWhole(deals, x => x.ValuePerRand);

// Direct comparison
Braai.IsBetterThan(dealA, dealB, x => x.ValuePerRand); // bool

Taxi — Collection & Async Utilities

Taxi logic applied to your data.

using SAToolKit.Controllers;

// Null coalescing (reference and value types)
Taxi.UTurn(nullableString, "fallback");
Taxi.UTurnValue(nullableInt, 0);

// Shuffle a list (Fisher-Yates)
var shuffled = Taxi.Rank(passengers);

// Capacity helpers
var first15  = Taxi.FilledUp(source, capacity: 15);
var seatsLeft = Taxi.SeatsLeft(current, capacity: 15);

// Predicate inversion
Taxi.IsGoingTheWrongWay(value, x => x.IsValid); // true when predicate fails

// Sorting & grouping
Taxi.SpeedSort(items, x => x.Name);
Taxi.SpeedSortDescending(items, x => x.Price);
Taxi.FastGroup(items, x => x.Category);

// Bulk operations
Taxi.BulkInsertFast(data, items => db.BulkInsert(items));
await Taxi.BulkInsertFastAsync(data, items => db.BulkInsertAsync(items));

// Exception handling with optional logging hooks
var result = Taxi.JumpRedLight(
    body: payload,
    riskyOperation: p => Process(p),
    onSuccessLog: p => logger.Info("done"),
    onErrorLog: (ex, p) => logger.Error(ex, "failed")
);

// Fire-and-forget parallel insert + update per item
await Taxi.RunFromCops(data, fastInsert, fastUpdate, onComplete: item => Log(item));

// Bulk parallel version
await Taxi.RunFromCopsBulk(data, bulkInsert, bulkUpdate);

Kullid — Safe Console Output

The SA Console.WriteLine. Sanitizes control characters before printing.

using SAToolKit.Controllers;

Kullid.Hoya("Hello SA");          // prints sanitized value, returns KullidOutput<T>
Kullid.AreYouMal("quiet please"); // prints in UPPERCASE
Kullid.Whisper("dev only");       // writes to Debug output only

Kullid.Niks(null);          // true  — null, empty string, or empty collection
Kullid.Niks("");            // true
Kullid.Niks(new List<int>()); // true
Kullid.Niks("something");   // false

Kullid.Skoon("\t dirty\x00string"); // sanitized string without printing

Sars — Telemetry & Security Scanning

They see everything. Audit everything. And they always want more information.

using SAToolKit.Controllers;
using SAToolKit.Models;

// Wrap a call — records elapsed time and heap allocations
var result = Sars.Audit("GetUser", () => db.GetUser(id));

// Async version
var result = await Sars.AuditAsync("FetchOrders", () => api.GetOrdersAsync());

// Security scanning — detects XSS, SQL injection, path traversal
SecurityScanResult scan = Sars.Scan(userInput);
// scan.IsSafe, scan.ThreatType, scan.MatchedPattern

bool safe = Sars.IsSafe(userInput); // quick boolean check

// Colour-coded console logging
Sars.Log("Starting job", SarsLogLevel.Info);
Sars.Log("Something looks off", SarsLogLevel.Warning);
Sars.Tax("This went very wrong"); // shorthand for Error level

// Telemetry report
SarsTelemetryReport report = Sars.GetReport();
// report.Entries — list of TelemetryEntry (name, duration, bytes, success, timestamp)

Sars.ClearTelemetry(); // call periodically in long-running services

Swap in a custom ISarsService for structured logging integrations:

Sars.Configure(new MyStructuredLoggingSarsService());

Cupcake — Async & Lazy Utilities

Results will come. Eventually.

using SAToolKit.Controllers;

// Lazy evaluation
var value = Cupcake.Eventually(() => ExpensiveComputation());

// Async with timeout — returns null if it takes too long
var data = await Cupcake.Deliver(() => api.GetDataAsync(), timeoutMs: 3000);

// Value type version
int? count = await Cupcake.DeliverValue(() => db.CountAsync(), timeoutMs: 2000);

// Null checks
Cupcake.IsStillWaiting(data);        // true when null
Cupcake.Promised(data, fallback);    // returns fallback when null

// Run many async operations with concurrency control
var results = await Cupcake.DeliverAll(
    actions: orderIds.Select(id => (Func<Task<Order>>)(() => GetOrderAsync(id))),
    timeoutMs: 5000,
    maxConcurrency: 10
);
// Returns only the operations that succeeded within the timeout

Springbok — Response Helpers

Success/failure utilities for HTTP and general result handling.

using SAToolKit.Controllers;

Springbok.IsSuccess(response, r => r.StatusCode == 200);
Springbok.IsFailure(response, r => r.StatusCode >= 400);

Springbok.IsSuccessStatusCode(200); // true
Springbok.IsErrorStatusCode(500);   // true

Springbok.GetMessageForStatusCode(200); // random success message
Springbok.GetMessageForStatusCode(404); // random error message

Springbok.GetSuccessMessage(); // e.g. "Operation completed successfully."
Springbok.GetErrorMessage();   // e.g. "An error occurred while processing your request."

Requirements

  • .NET 10.0+

License

MIT © ReeceMas

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 126 6/15/2026