(feature): init
This commit is contained in:
commit
8ac380c7d4
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
262
.gitignore
vendored
Normal file
262
.gitignore
vendored
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
#*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
197
README.md
Normal file
197
README.md
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# MaksIT.Core
|
||||||
|
|
||||||
|
MaksIT.Core is a collection of helper methods and extensions for .NET projects, designed to simplify common tasks and improve code readability. The library includes extensions for `Guid`, `string`, `Object`, and a base class for creating enumeration types.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Enumeration](#enumeration)
|
||||||
|
- [Guid Extensions](#guid-extensions)
|
||||||
|
- [Object Extensions](#object-extensions)
|
||||||
|
- [String Extensions](#string-extensions)
|
||||||
|
- [Available Methods](#available-methods)
|
||||||
|
- [Enumeration Methods](#enumeration-methods)
|
||||||
|
- [Guid Methods](#guid-methods)
|
||||||
|
- [Object Methods](#object-methods)
|
||||||
|
- [String Methods](#string-methods)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install MaksIT.Core, add the package to your project via NuGet:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
dotnet add package MaksIT.Core
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually add it to your `.csproj` file:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PackageReference Include="MaksIT.Core" Version="1.0.0" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Enumeration
|
||||||
|
|
||||||
|
The `Enumeration` base class provides a way to create strongly-typed enums in C#. This is useful for scenarios where you need more functionality than the default `enum` type.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class Status : Enumeration
|
||||||
|
{
|
||||||
|
public static readonly Status Active = new Status(1, "Active");
|
||||||
|
public static readonly Status Inactive = new Status(2, "Inactive");
|
||||||
|
|
||||||
|
private Status(int id, string name) : base(id, name) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
var activeStatus = Status.FromValue<Status>(1);
|
||||||
|
Console.WriteLine(activeStatus.Name); // Output: Active
|
||||||
|
```
|
||||||
|
|
||||||
|
### Guid Extensions
|
||||||
|
|
||||||
|
The `GuidExtensions` class contains extensions for working with `Guid` types.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Guid guid = Guid.NewGuid();
|
||||||
|
Guid? nullableGuid = guid.ToNullable();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Extensions
|
||||||
|
|
||||||
|
The `ObjectExtensions` class provides extensions for working with objects.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var person = new { Name = "John", Age = 30 };
|
||||||
|
string json = person.ToJson();
|
||||||
|
Console.WriteLine(json); // Output: {"name":"John","age":30}
|
||||||
|
```
|
||||||
|
|
||||||
|
### String Extensions
|
||||||
|
|
||||||
|
The `StringExtensions` class provides a variety of useful string manipulation methods.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string text = "Hello World";
|
||||||
|
bool isLike = text.Like("Hello*"); // SQL-like matching
|
||||||
|
Console.WriteLine(isLike); // Output: True
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Methods
|
||||||
|
|
||||||
|
### Enumeration Methods
|
||||||
|
|
||||||
|
- **`GetAll<T>()`**: Retrieves all static fields of a given type `T` that derive from `Enumeration`.
|
||||||
|
- **`Equals(object? obj)`**: Determines whether the specified object is equal to the current object.
|
||||||
|
- **`GetHashCode()`**: Returns the hash code for the current object.
|
||||||
|
- **`AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)`**: Computes the absolute difference between two enumeration values.
|
||||||
|
- **`FromValue<T>(int value)`**: Retrieves an instance of type `T` from its integer value.
|
||||||
|
- **`FromDisplayName<T>(string displayName)`**: Retrieves an instance of type `T` from its display name.
|
||||||
|
- **`CompareTo(object? other)`**: Compares the current instance with another object of the same type.
|
||||||
|
|
||||||
|
### Guid Methods
|
||||||
|
|
||||||
|
- **`ToNullable(this Guid id)`**: Converts a `Guid` to a nullable `Guid?`. Returns `null` if the `Guid` is `Guid.Empty`.
|
||||||
|
|
||||||
|
### Object Methods
|
||||||
|
|
||||||
|
- **`ToJson<T>(this T? obj)`**: Converts an object to a JSON string using default serialization options.
|
||||||
|
- **`ToJson<T>(this T? obj, List<JsonConverter>? converters)`**: Converts an object to a JSON string using custom converters.
|
||||||
|
|
||||||
|
### String Methods
|
||||||
|
|
||||||
|
- **`Like(this string? text, string? wildcardedText)`**: Determines if a string matches a given wildcard pattern (SQL LIKE).
|
||||||
|
- **`Left(this string s, int count)`**: Returns the left substring of the specified length.
|
||||||
|
- **`Right(this string s, int count)`**: Returns the right substring of the specified length.
|
||||||
|
- **`Mid(this string s, int index, int count)`**: Returns a substring starting from the specified index with the specified length.
|
||||||
|
- **`ToInteger(this string s)`**: Converts a string to an integer, returning zero if conversion fails.
|
||||||
|
- **`IsInteger(this string s)`**: Determines whether the string represents an integer.
|
||||||
|
- **`Prepend(this StringBuilder sb, string content)`**: Prepends content to the beginning of a `StringBuilder`.
|
||||||
|
- **`ToEnum<T>(this string input)`**: Converts a string to an enum value of type `T`.
|
||||||
|
- **`ToNullableEnum<T>(this string input)`**: Converts a string to a nullable enum value of type `T`.
|
||||||
|
- **`ToNull(this string s)`**: Returns `null` if the string is empty or whitespace.
|
||||||
|
- **`NullIfEmptyString(this string s)`**: Returns `null` if the string is empty or whitespace, otherwise returns the original string.
|
||||||
|
- **`ToLong(this string s)`**: Converts a string to a long, returning a hash code if conversion fails.
|
||||||
|
- **`ToNullableLong(this string s)`**: Converts a string to a nullable long, returning `null` if conversion fails.
|
||||||
|
- **`ToInt(this string s)`**: Converts a string to an int, returning a hash code if conversion fails.
|
||||||
|
- **`ToNullableInt(this string s)`**: Converts a string to a nullable int, returning `null` if conversion fails.
|
||||||
|
- **`ToUint(this string s)`**: Converts a string to a uint, returning a hash code if conversion fails.
|
||||||
|
- **`ToNullableUint(this string s)`**: Converts a string to a nullable uint, returning `null` if conversion fails.
|
||||||
|
- **`ToDecimal(this string s)`**: Converts a string to a decimal, returning a hash code if conversion fails.
|
||||||
|
- **`ToNullableDecimal(this string s)`**: Converts a string to a nullable decimal, returning `null` if conversion fails.
|
||||||
|
- **`ToDouble(this string s)`**: Converts a string to a double, returning a hash code if conversion fails.
|
||||||
|
- **`ToNullableDouble(this string s)`**: Converts a string to a nullable double, returning `null` if conversion fails.
|
||||||
|
- **`ToDate(this string s, string[] formats)`**: Converts a string to a `DateTime` object using a specified format.
|
||||||
|
- **`ToDate(this string s)`**: Converts a string to a `DateTime` object using the default format.
|
||||||
|
- **`ToNullableDate(this string s)`**: Converts a string to a nullable `DateTime` object using the default format.
|
||||||
|
- **`ToNullableDate(this string s, string[] formats)`**: Converts a string to a nullable `DateTime` object using specified formats.
|
||||||
|
- **`ToDateTime(this string s, string[] formats)`**: Converts a string to a `DateTime` object using specified formats.
|
||||||
|
- **`ToDateTime(this string s)`**: Converts a string to a `DateTime` object using the default formats.
|
||||||
|
- **`ToNullableDateTime(this string s)`**: Converts a string to a nullable `DateTime` object using the default formats.
|
||||||
|
- **`ToNullableDateTime(this string s, string[] formats)`**: Converts a string to a nullable `DateTime` object using specified formats.
|
||||||
|
- **`ToBool(this string s)`**: Converts a string to a boolean.
|
||||||
|
- **`ToNullableBool(this string s)`**: Converts a string to a nullable boolean.
|
||||||
|
- **`ToGuid(this string text)`**: Converts a string to a `Guid`.
|
||||||
|
- **`ToNullableGuid(this string s)`**: Converts a string to a nullable `Guid`.
|
||||||
|
- **`StringSplit(this string s, char c)`**: Splits a string by a specified character and trims each resulting element.
|
||||||
|
- **`ToTitle(this string s)`**: Converts the first character of the string to uppercase.
|
||||||
|
- **`ExtractUrls(this string s)`**: Extracts all URLs from a string.
|
||||||
|
- **`Format(this string s, params object[] args)`**: Formats a string using specified arguments.
|
||||||
|
- **`Excerpt(this string s, int length = 60)`**: Truncates a string to a specified length, adding ellipses if necessary.
|
||||||
|
- **`ToObject<T>(this string s)`**: Deserializes a JSON string into an object of type `T`.
|
||||||
|
- **`ToObject<T>(this string s, List<JsonConverter> converters)`**: Deserializes a JSON string into an object of type `T` using custom converters.
|
||||||
|
- **`IsValidEmail(this string? s)`**: Validates whether the string is a valid email format.
|
||||||
|
- **`HtmlToPlainText(this string htmlCode)`**: Converts HTML content to plain text.
|
||||||
|
- **`ToCamelCase(this string input)`**: Converts a string to camel case.
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
Contributions to this project are welcome! Please fork the repository and submit a pull request with your changes. If you encounter any issues or have feature requests, feel free to open an issue on GitHub.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the full license text below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### MIT License
|
||||||
|
|
||||||
|
```
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Maksym Sadovnychyy (MAKS-IT)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
For any questions or inquiries, please reach out via GitHub or [email](mailto:maksym.sadovnychyy@gmail.com).
|
||||||
165
src/MaksIT.Core.Tests/Abstractions/EnumerationTests.cs
Normal file
165
src/MaksIT.Core.Tests/Abstractions/EnumerationTests.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Abstractions.Tests {
|
||||||
|
|
||||||
|
public class TestEnumeration : Enumeration {
|
||||||
|
public static readonly TestEnumeration First = new TestEnumeration(1, "First");
|
||||||
|
public static readonly TestEnumeration Second = new TestEnumeration(2, "Second");
|
||||||
|
public static readonly TestEnumeration Third = new TestEnumeration(3, "Third");
|
||||||
|
|
||||||
|
public TestEnumeration(int id, string name) : base(id, name) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class EnumerationTests {
|
||||||
|
[Fact]
|
||||||
|
public void GetAll_ShouldReturnAllEnumerations() {
|
||||||
|
// Act
|
||||||
|
var allValues = Enumeration.GetAll<TestEnumeration>().ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(allValues);
|
||||||
|
Assert.Equal(3, allValues.Count);
|
||||||
|
Assert.Contains(TestEnumeration.First, allValues);
|
||||||
|
Assert.Contains(TestEnumeration.Second, allValues);
|
||||||
|
Assert.Contains(TestEnumeration.Third, allValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1, "First")]
|
||||||
|
[InlineData(2, "Second")]
|
||||||
|
[InlineData(3, "Third")]
|
||||||
|
public void FromValue_ShouldReturnEnumerationByValue(int id, string expectedName) {
|
||||||
|
// Act
|
||||||
|
var result = Enumeration.FromValue<TestEnumeration>(id);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(expectedName, result.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("First", 1)]
|
||||||
|
[InlineData("Second", 2)]
|
||||||
|
[InlineData("Third", 3)]
|
||||||
|
public void FromDisplayName_ShouldReturnEnumerationByName(string displayName, int expectedId) {
|
||||||
|
// Act
|
||||||
|
var result = Enumeration.FromDisplayName<TestEnumeration>(displayName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(expectedId, result.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AbsoluteDifference_ShouldReturnCorrectDifference() {
|
||||||
|
// Act
|
||||||
|
var difference = Enumeration.AbsoluteDifference(TestEnumeration.First, TestEnumeration.Third);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, difference);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_SameReference_ShouldReturnTrue() {
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.Equals(TestEnumeration.First);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_DifferentReferencesSameValues_ShouldReturnTrue() {
|
||||||
|
// Arrange
|
||||||
|
var firstCopy = Enumeration.FromValue<TestEnumeration>(1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.Equals(firstCopy);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_DifferentValues_ShouldReturnFalse() {
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.Equals(TestEnumeration.Second);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CompareTo_ShouldReturnZeroForEqualValues() {
|
||||||
|
// Arrange
|
||||||
|
var firstCopy = Enumeration.FromValue<TestEnumeration>(1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.CompareTo(firstCopy);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(0, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CompareTo_ShouldReturnPositiveForGreaterValue() {
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.Second.CompareTo(TestEnumeration.First);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CompareTo_ShouldReturnNegativeForLesserValue() {
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.CompareTo(TestEnumeration.Second);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CompareTo_InvalidComparison_ShouldThrowArgumentException() {
|
||||||
|
// Arrange
|
||||||
|
var nonEnumerationObject = new object();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => TestEnumeration.First.CompareTo(nonEnumerationObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_ShouldReturnIdHashCode() {
|
||||||
|
// Act
|
||||||
|
var hashCode = TestEnumeration.First.GetHashCode();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(TestEnumeration.First.Id.GetHashCode(), hashCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_ShouldReturnName() {
|
||||||
|
// Act
|
||||||
|
var result = TestEnumeration.First.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("First", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Parse_InvalidValue_ShouldThrowInvalidOperationException() {
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() => Enumeration.FromValue<TestEnumeration>(999));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Parse_InvalidDisplayName_ShouldThrowInvalidOperationException() {
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() => Enumeration.FromDisplayName<TestEnumeration>("NonExistent"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/MaksIT.Core.Tests/Extensions/GuidExtensionsTests.cs
Normal file
72
src/MaksIT.Core.Tests/Extensions/GuidExtensionsTests.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Extensions.Tests {
|
||||||
|
public class GuidExtensionsTests {
|
||||||
|
[Fact]
|
||||||
|
public void ToNullable_WithEmptyGuid_ShouldReturnNull() {
|
||||||
|
// Arrange
|
||||||
|
var emptyGuid = Guid.Empty;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = emptyGuid.ToNullable();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToNullable_WithNonEmptyGuid_ShouldReturnSameGuid() {
|
||||||
|
// Arrange
|
||||||
|
var nonEmptyGuid = Guid.NewGuid();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = nonEmptyGuid.ToNullable();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(nonEmptyGuid, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToNullable_WithDefaultGuid_ShouldReturnNull() {
|
||||||
|
// Arrange
|
||||||
|
var defaultGuid = default(Guid);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = defaultGuid.ToNullable();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToNullable_WithSameGuidTwice_ShouldReturnSameGuidEachTime() {
|
||||||
|
// Arrange
|
||||||
|
var guid = Guid.NewGuid();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = guid.ToNullable();
|
||||||
|
var result2 = guid.ToNullable();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToNullable_WithMultipleNewGuids_ShouldReturnUniqueNonEmptyResults() {
|
||||||
|
// Arrange
|
||||||
|
var guid1 = Guid.NewGuid();
|
||||||
|
var guid2 = Guid.NewGuid();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = guid1.ToNullable();
|
||||||
|
var result2 = guid2.ToNullable();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotEqual(result1, result2);
|
||||||
|
Assert.NotNull(result1);
|
||||||
|
Assert.NotNull(result2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/MaksIT.Core.Tests/Extensions/ObjectExtensionsTests.cs
Normal file
122
src/MaksIT.Core.Tests/Extensions/ObjectExtensionsTests.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using MaksIT.Core.Extensions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Tests.Extensions {
|
||||||
|
public class ObjectExtensionsTests {
|
||||||
|
private class TestObject {
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public int Age { get; set; }
|
||||||
|
public string? Address { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomDateTimeConverter : JsonConverter<DateTime> {
|
||||||
|
private readonly string _format;
|
||||||
|
|
||||||
|
public CustomDateTimeConverter(string format) {
|
||||||
|
_format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
||||||
|
var dateString = reader.GetString();
|
||||||
|
if (dateString is null) {
|
||||||
|
throw new JsonException("Expected a date string but got null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime.ParseExact(dateString, _format, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) {
|
||||||
|
writer.WriteStringValue(value.ToString(_format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithNullObject_ShouldReturnEmptyJson() {
|
||||||
|
// Arrange
|
||||||
|
TestObject? obj = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("{}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithSimpleObject_ShouldReturnCorrectJson() {
|
||||||
|
// Arrange
|
||||||
|
var obj = new TestObject {
|
||||||
|
Name = "John Doe",
|
||||||
|
Age = 30
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("{\"name\":\"John Doe\",\"age\":30}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithObjectAndNullValues_ShouldIgnoreNullProperties() {
|
||||||
|
// Arrange
|
||||||
|
var obj = new TestObject {
|
||||||
|
Name = "John Doe",
|
||||||
|
Age = 30,
|
||||||
|
Address = null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("{\"name\":\"John Doe\",\"age\":30}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithCustomJsonConverter_ShouldApplyConverter() {
|
||||||
|
// Arrange
|
||||||
|
var obj = new DateTime(2023, 08, 30);
|
||||||
|
var converters = new List<JsonConverter> { new CustomDateTimeConverter("yyyy-MM-dd") };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson(converters);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("\"2023-08-30\"", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithComplexObjectAndConverters_ShouldSerializeCorrectly() {
|
||||||
|
// Arrange
|
||||||
|
var obj = new {
|
||||||
|
Name = "Jane Doe",
|
||||||
|
BirthDate = new DateTime(1990, 12, 25)
|
||||||
|
};
|
||||||
|
|
||||||
|
var converters = new List<JsonConverter> { new CustomDateTimeConverter("yyyy/MM/dd") };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson(converters);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("{\"name\":\"Jane Doe\",\"birthDate\":\"1990/12/25\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToJson_WithEmptyObject_ShouldReturnEmptyJsonObject() {
|
||||||
|
// Arrange
|
||||||
|
var obj = new { };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = obj.ToJson();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("{}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
244
src/MaksIT.Core.Tests/Extensions/StringExtensionsTests.cs
Normal file
244
src/MaksIT.Core.Tests/Extensions/StringExtensionsTests.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using MaksIT.Core.Extensions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Tests.Extensions {
|
||||||
|
public class StringExtensionsTests {
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Hello World", "H*", true)] // Match starts with 'H'
|
||||||
|
[InlineData("Hello World", "h*", true)] // Case insensitive match
|
||||||
|
[InlineData("Hello World", "*World", true)] // Match ends with 'World'
|
||||||
|
[InlineData("Hello World", "Hello?World", true)] // '?' should match exactly one character (space in this case)
|
||||||
|
[InlineData("Hello World", "*W?rld", true)] // '?' matches 'o' in 'World'
|
||||||
|
[InlineData("Hello World", "Goodbye*", false)] // No match for 'Goodbye*'
|
||||||
|
public void Like_ShouldReturnExpectedResults(string input, string pattern, bool expected) {
|
||||||
|
// Act
|
||||||
|
bool result = input.Like(pattern);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abcdef", 3, "abc")]
|
||||||
|
[InlineData("abcdef", 0, "")]
|
||||||
|
[InlineData("abcdef", 10, "abcdef")]
|
||||||
|
public void Left_ShouldReturnLeftSubstring(string input, int count, string expected) {
|
||||||
|
// Act
|
||||||
|
string result = input.Left(count);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abcdef", 3, "def")]
|
||||||
|
[InlineData("abcdef", 0, "")]
|
||||||
|
[InlineData("abcdef", 10, "abcdef")]
|
||||||
|
public void Right_ShouldReturnRightSubstring(string input, int count, string expected) {
|
||||||
|
// Act
|
||||||
|
string result = input.Right(count);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abcdef", 1, 3, "bcd")]
|
||||||
|
[InlineData("abcdef", 0, 2, "ab")]
|
||||||
|
[InlineData("abcdef", 4, 10, "ef")]
|
||||||
|
[InlineData("abcdef", 6, 2, "")]
|
||||||
|
public void Mid_ShouldReturnSubstring(string input, int index, int count, string expected) {
|
||||||
|
// Act
|
||||||
|
string result = input.Mid(index, count);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("123", 123)]
|
||||||
|
[InlineData("abc", 0)]
|
||||||
|
[InlineData(null, 0)]
|
||||||
|
[InlineData("", 0)]
|
||||||
|
public void ToInteger_ShouldConvertToInteger(string input, int expected) {
|
||||||
|
// Act
|
||||||
|
int result = input.ToInteger();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("123", true)]
|
||||||
|
[InlineData("-123", true)]
|
||||||
|
[InlineData("abc", false)]
|
||||||
|
[InlineData("123abc", false)]
|
||||||
|
public void IsInteger_ShouldReturnIfStringIsInteger(string input, bool expected) {
|
||||||
|
// Act
|
||||||
|
bool result = input.IsInteger();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Prepend_ShouldPrependStringToStringBuilder() {
|
||||||
|
// Arrange
|
||||||
|
var sb = new StringBuilder("World");
|
||||||
|
string content = "Hello ";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
sb.Prepend(content);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Hello World", sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1", DayOfWeek.Monday)]
|
||||||
|
[InlineData("Tuesday", DayOfWeek.Tuesday)]
|
||||||
|
[InlineData("5", DayOfWeek.Friday)]
|
||||||
|
public void ToEnum_ShouldConvertStringToEnum(string input, DayOfWeek expected) {
|
||||||
|
// Act
|
||||||
|
DayOfWeek result = input.ToEnum<DayOfWeek>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToEnum_InvalidValue_ShouldThrowNotSupportedException() {
|
||||||
|
// Arrange
|
||||||
|
string input = "NotAnEnumValue";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<NotSupportedException>(() => input.ToEnum<DayOfWeek>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(" ", null)]
|
||||||
|
[InlineData("", null)]
|
||||||
|
[InlineData("valid", "valid")]
|
||||||
|
public void ToNull_ShouldReturnNullForWhitespaceOrEmptyString(string input, string expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToNull();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("123", 123L)]
|
||||||
|
public void ToLong_ShouldConvertToLong_WhenValidLong(string input, long? expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToLong();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abc", null)]
|
||||||
|
[InlineData("", null)]
|
||||||
|
public void ToLong_ShouldReturnNull_WhenInvalidLong(string input, long? expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToLong();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("2021-08-30", new[] { "yyyy-MM-dd" }, "2021-08-30T00:00:00Z")]
|
||||||
|
[InlineData("30/08/2021", new[] { "dd/MM/yyyy" }, "2021-08-30T00:00:00Z")]
|
||||||
|
public void ToDate_ShouldConvertToDate(string input, string[] formats, string expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToDate(formats);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(DateTime.Parse(expected, null, DateTimeStyles.RoundtripKind), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("2021-08-30T00:00:00Z", "2021-08-30T00:00:00Z")]
|
||||||
|
[InlineData("Now", "Now")]
|
||||||
|
public void ToDateTime_ShouldConvertToDateTime(string input, string expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToDateTime();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if (expected == "Now") {
|
||||||
|
Assert.Equal(DateTime.Now.ToString("dd/MM/yyyy"), result.ToString("dd/MM/yyyy"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert.Equal(DateTime.Parse(expected, null, DateTimeStyles.RoundtripKind), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("ok", true)]
|
||||||
|
[InlineData("yes", true)]
|
||||||
|
[InlineData("true", true)]
|
||||||
|
[InlineData("1", true)]
|
||||||
|
[InlineData("no", false)]
|
||||||
|
[InlineData("false", false)]
|
||||||
|
[InlineData("0", false)]
|
||||||
|
[InlineData("invalid", false)]
|
||||||
|
public void ToBool_ShouldConvertToBool(string input, bool expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToBool();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("e02fd0e4-00fd-090A-ca30-0d00a0038ba0", "e02fd0e4-00fd-090a-ca30-0d00a0038ba0")]
|
||||||
|
[InlineData("invalid-guid", null)]
|
||||||
|
public void ToGuid_ShouldConvertStringToGuid(string input, string expected) {
|
||||||
|
// Act
|
||||||
|
if (expected == null) {
|
||||||
|
var result = input.ToGuid();
|
||||||
|
|
||||||
|
// Assert that it returns a valid Guid (from MD5 hash, not an exception)
|
||||||
|
Assert.IsType<Guid>(result);
|
||||||
|
Assert.NotEqual(Guid.Empty, result); // Check that it does not return Guid.Empty
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var result = input.ToGuid();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(Guid.Parse(expected), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("<p>Hello World</p>", "Hello World")]
|
||||||
|
[InlineData("<script>alert('test');</script><p>Hello</p>", "Hello")]
|
||||||
|
public void HtmlToPlainText_ShouldConvertHtmlToPlainText(string input, string expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.HtmlToPlainText();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("hello world", "helloWorld")]
|
||||||
|
[InlineData("Hello World", "helloWorld")]
|
||||||
|
[InlineData("HELLO_WORLD", "helloWorld")]
|
||||||
|
[InlineData("HELLO-WORLD", "helloWorld")]
|
||||||
|
public void ToCamelCase_ShouldConvertToCamelCase(string input, string expected) {
|
||||||
|
// Act
|
||||||
|
var result = input.ToCamelCase();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/MaksIT.Core.Tests/MaksIT.Core.Tests.csproj
Normal file
27
src/MaksIT.Core.Tests/MaksIT.Core.Tests.csproj
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MaksIT.Core\MaksIT.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
31
src/MaksIT.Core.sln
Normal file
31
src/MaksIT.Core.sln
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.Core", "MaksIT.Core\MaksIT.Core.csproj", "{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaksIT.Core.Tests", "MaksIT.Core.Tests\MaksIT.Core.Tests.csproj", "{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {9BCC72D1-8BE8-4924-AF73-C8E86E16EC59}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
50
src/MaksIT.Core/Abstractions/Enumeration.cs
Normal file
50
src/MaksIT.Core/Abstractions/Enumeration.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Abstractions {
|
||||||
|
public abstract class Enumeration : IComparable {
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
protected Enumeration(int id, string name) => (Id, Name) = (id, name);
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
|
||||||
|
public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
|
||||||
|
typeof(T).GetFields(BindingFlags.Public |
|
||||||
|
BindingFlags.Static |
|
||||||
|
BindingFlags.DeclaredOnly)
|
||||||
|
.Select(f => f.GetValue(null))
|
||||||
|
.Cast<T>();
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) =>
|
||||||
|
obj is Enumeration otherValue &&
|
||||||
|
GetType() == obj.GetType() &&
|
||||||
|
Id == otherValue.Id;
|
||||||
|
|
||||||
|
public override int GetHashCode() => Id.GetHashCode();
|
||||||
|
|
||||||
|
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) =>
|
||||||
|
Math.Abs(firstValue.Id - secondValue.Id);
|
||||||
|
|
||||||
|
public static T FromValue<T>(int value) where T : Enumeration =>
|
||||||
|
Parse<T, int>(value, nameof(value), item => item.Id == value);
|
||||||
|
|
||||||
|
public static T FromDisplayName<T>(string displayName) where T : Enumeration =>
|
||||||
|
Parse<T, string>(displayName, nameof(displayName), item => item.Name == displayName);
|
||||||
|
|
||||||
|
private static T Parse<T, TK>(TK value, string description, Func<T, bool> predicate) where T : Enumeration =>
|
||||||
|
GetAll<T>().FirstOrDefault(predicate) ??
|
||||||
|
throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");
|
||||||
|
|
||||||
|
public int CompareTo(object? other) {
|
||||||
|
if (other is Enumeration otherEnumeration)
|
||||||
|
return Id.CompareTo(otherEnumeration.Id);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Object is not of type {nameof(Enumeration)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/MaksIT.Core/Extensions/GuidExtensions.cs
Normal file
15
src/MaksIT.Core/Extensions/GuidExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Extensions {
|
||||||
|
public static class GuidExtensions {
|
||||||
|
public static Guid? ToNullable(this Guid id) {
|
||||||
|
// Return null if the Guid is the default value (Guid.Empty)
|
||||||
|
return id == default ? null : id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
src/MaksIT.Core/Extensions/ObjectExtensions.cs
Normal file
36
src/MaksIT.Core/Extensions/ObjectExtensions.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Extensions;
|
||||||
|
|
||||||
|
public static class ObjectExtensions {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts object to json string
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToJson<T>(this T? obj) => obj.ToJson(null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts object to json string
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="converters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToJson<T>(this T? obj, List<JsonConverter>? converters) {
|
||||||
|
if (obj == null)
|
||||||
|
return "{}";
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions {
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
converters?.ForEach(x => options.Converters.Add(x));
|
||||||
|
|
||||||
|
return JsonSerializer.Serialize(obj, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
232
src/MaksIT.Core/Extensions/StringExtensions.cs
Normal file
232
src/MaksIT.Core/Extensions/StringExtensions.cs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace MaksIT.Core.Extensions {
|
||||||
|
public static partial class StringExtensions {
|
||||||
|
/// <summary>
|
||||||
|
/// SQL Like implementation using wildcard patterns.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Like(this string? text, string? wildcardedText) {
|
||||||
|
if (text is null || wildcardedText is null) return false;
|
||||||
|
|
||||||
|
return Regex.IsMatch(text, wildcardedText.WildcardToRegular(), RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a wildcarded string to a regular expression.
|
||||||
|
/// </summary>
|
||||||
|
private static string WildcardToRegular(this string value) =>
|
||||||
|
$"^{Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*")}$";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the left substring of the specified length.
|
||||||
|
/// </summary>
|
||||||
|
public static string Left(this string s, int count) =>
|
||||||
|
s.Substring(0, Math.Min(count, s.Length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the right substring of the specified length.
|
||||||
|
/// </summary>
|
||||||
|
public static string Right(this string s, int count) =>
|
||||||
|
s.Substring(Math.Max(0, s.Length - count));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a substring starting from the specified index with the specified length.
|
||||||
|
/// </summary>
|
||||||
|
public static string Mid(this string s, int index, int count) =>
|
||||||
|
s.Substring(index, Math.Min(count, s.Length - index));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the string to an integer, returning zero if conversion fails.
|
||||||
|
/// </summary>
|
||||||
|
public static int ToInteger(this string s) =>
|
||||||
|
int.TryParse(s, out var integerValue) ? integerValue : 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the string represents an integer.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsInteger(this string s) => Regex.IsMatch(s, @"^-?\d+$");
|
||||||
|
|
||||||
|
public static StringBuilder Prepend(this StringBuilder sb, string content) => sb.Insert(0, content);
|
||||||
|
|
||||||
|
public static T ToEnum<T>(this string input) where T : struct {
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
throw new ArgumentException("Input cannot be null or empty.", nameof(input));
|
||||||
|
|
||||||
|
if (Enum.TryParse(input, true, out T result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
var enumType = typeof(T);
|
||||||
|
|
||||||
|
foreach (T enumItem in Enum.GetValues(enumType)) {
|
||||||
|
var att = enumType.GetMember(enumItem.ToString() ?? string.Empty)[0]
|
||||||
|
.GetCustomAttributes(typeof(DisplayAttribute), false)
|
||||||
|
.SingleOrDefault() as DisplayAttribute;
|
||||||
|
|
||||||
|
var displayName = att?.GetName();
|
||||||
|
|
||||||
|
if (input.Equals(displayName, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
return enumItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Cannot parse the value '{input}' for {enumType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? ToNullableEnum<T>(this string input) where T : struct =>
|
||||||
|
!string.IsNullOrWhiteSpace(input) ? input.ToEnum<T>() : null;
|
||||||
|
|
||||||
|
public static string? ToNull(this string s) => string.IsNullOrWhiteSpace(s) ? null : s;
|
||||||
|
|
||||||
|
public static string? NullIfEmptyString(this string s) => s.ToNull();
|
||||||
|
|
||||||
|
public static long? ToLong(this string s) =>
|
||||||
|
long.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out var result) ? result : (long?)null;
|
||||||
|
|
||||||
|
public static long? ToNullableLong(this string s) => string.IsNullOrWhiteSpace(s) ? (long?)null : s.ToLong();
|
||||||
|
|
||||||
|
public static int? ToInt(this string s) =>
|
||||||
|
int.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out var result) ? result : (int?)null;
|
||||||
|
|
||||||
|
public static int? ToNullableInt(this string s) => string.IsNullOrWhiteSpace(s) ? (int?)null : s.ToInt();
|
||||||
|
|
||||||
|
public static uint? ToUint(this string s) =>
|
||||||
|
uint.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out var result) ? result : (uint?)null;
|
||||||
|
|
||||||
|
public static uint? ToNullableUint(this string s) => string.IsNullOrWhiteSpace(s) ? (uint?)null : s.ToUint();
|
||||||
|
|
||||||
|
public static decimal? ToDecimal(this string s) =>
|
||||||
|
decimal.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out var result) ? result : (decimal?)null;
|
||||||
|
|
||||||
|
public static decimal? ToNullableDecimal(this string s) => string.IsNullOrWhiteSpace(s) ? (decimal?)null : s.ToDecimal();
|
||||||
|
|
||||||
|
public static double? ToDouble(this string s) =>
|
||||||
|
double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out var result) ? result : (double?)null;
|
||||||
|
|
||||||
|
public static double? ToNullableDouble(this string s) => string.IsNullOrWhiteSpace(s) ? (double?)null : s.ToDouble();
|
||||||
|
|
||||||
|
#region DateTime
|
||||||
|
public static DateTime ToDate(this string s, string[] formats) =>
|
||||||
|
DateTime.TryParseExact(s, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var datetime)
|
||||||
|
? DateTime.SpecifyKind(datetime, DateTimeKind.Utc)
|
||||||
|
: throw new FormatException($"The date [{s}] is not in the required format: [{formats[0]}]");
|
||||||
|
|
||||||
|
public static DateTime ToDate(this string s) => s.ToDate(new[] { "dd/MM/yyyy" });
|
||||||
|
|
||||||
|
public static DateTime? ToNullableDate(this string s) => string.IsNullOrEmpty(s) ? (DateTime?)null : s.ToDate();
|
||||||
|
|
||||||
|
public static DateTime? ToNullableDate(this string s, string[] formats) => string.IsNullOrEmpty(s) ? (DateTime?)null : s.ToDate(formats);
|
||||||
|
|
||||||
|
public static DateTime ToDateTime(this string s, string[] formats) {
|
||||||
|
if (s.Equals("Now", StringComparison.OrdinalIgnoreCase)) return DateTime.Now;
|
||||||
|
if (s.Equals("UtcNow", StringComparison.OrdinalIgnoreCase)) return DateTime.UtcNow;
|
||||||
|
if (s.Equals("Today", StringComparison.OrdinalIgnoreCase)) return DateTime.Today;
|
||||||
|
|
||||||
|
return DateTime.TryParseExact(s, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)
|
||||||
|
? DateTime.SpecifyKind(result, DateTimeKind.Utc)
|
||||||
|
: throw new FormatException($"Unable to parse exact date from value [{s}] with formats [{string.Join(", ", formats)}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime ToDateTime(this string s) => s.ToDateTime(new[] { "dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss", "dd/MM/yyyy HH:mm", "yyyy-MM-dd'T'HH:mm:ss'Z'" });
|
||||||
|
|
||||||
|
public static DateTime? ToNullableDateTime(this string s) => string.IsNullOrWhiteSpace(s) ? (DateTime?)null : s.ToDateTime();
|
||||||
|
|
||||||
|
public static DateTime? ToNullableDateTime(this string s, string[] formats) => string.IsNullOrWhiteSpace(s) ? (DateTime?)null : s.ToDateTime(formats);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static bool ToBool(this string s) =>
|
||||||
|
new[] { "ok", "yes", "y", "true", "1" }.Contains(s, StringComparer.InvariantCultureIgnoreCase);
|
||||||
|
|
||||||
|
public static bool? ToNullableBool(this string s) => string.IsNullOrWhiteSpace(s) ? (bool?)null : s.ToBool();
|
||||||
|
|
||||||
|
public static Guid ToGuid(this string text) =>
|
||||||
|
Guid.TryParse(text, out var value) ? value : new Guid(MD5.Create().ComputeHash(Encoding.Default.GetBytes(text.ToUpper())));
|
||||||
|
|
||||||
|
public static Guid? ToNullableGuid(this string s) => string.IsNullOrWhiteSpace(s) ? (Guid?)null : s.ToGuid();
|
||||||
|
|
||||||
|
public static string[] StringSplit(this string s, char c) =>
|
||||||
|
s.Split(c).Select(x => x.Trim()).ToArray();
|
||||||
|
|
||||||
|
public static string ToTitle(this string s) => string.IsNullOrWhiteSpace(s) ? s : char.ToUpper(s[0]) + s[1..];
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])", RegexOptions.Compiled)]
|
||||||
|
private static partial Regex UrlsRegex();
|
||||||
|
|
||||||
|
public static IEnumerable<Uri> ExtractUrls(this string s) =>
|
||||||
|
UrlsRegex().Matches(s).Cast<Match>()
|
||||||
|
.Select(match => match.Value)
|
||||||
|
.Where(url => Uri.TryCreate(url, UriKind.Absolute, out _))
|
||||||
|
.Select(url => new Uri(url))
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
public static string Format(this string s, params object[] args) => string.Format(s, args);
|
||||||
|
|
||||||
|
public static string Excerpt(this string s, int length = 60) =>
|
||||||
|
string.IsNullOrWhiteSpace(s) ? s : s.Length <= length ? s : $"{s.Substring(0, length - 3)}...";
|
||||||
|
|
||||||
|
public static T? ToObject<T>(this string s) => ToObjectCore<T>(s, null);
|
||||||
|
|
||||||
|
public static T? ToObject<T>(this string s, List<JsonConverter> converters) => ToObjectCore<T>(s, converters);
|
||||||
|
|
||||||
|
private static T? ToObjectCore<T>(string s, List<JsonConverter>? converters) {
|
||||||
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||||
|
converters?.ForEach(x => options.Converters.Add(x));
|
||||||
|
return JsonSerializer.Deserialize<T>(s, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsValidEmail(this string? s) {
|
||||||
|
if (s is null) return false;
|
||||||
|
|
||||||
|
const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
|
||||||
|
var regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, TimeSpan.FromSeconds(2));
|
||||||
|
|
||||||
|
return regex.IsMatch(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string HtmlToPlainText(this string htmlCode) {
|
||||||
|
if (string.IsNullOrEmpty(htmlCode))
|
||||||
|
return htmlCode;
|
||||||
|
|
||||||
|
var sb = new StringBuilder(htmlCode);
|
||||||
|
|
||||||
|
// Remove new lines, tabs, and multiple spaces
|
||||||
|
sb.Replace("\n", " ").Replace("\t", " ");
|
||||||
|
sb = new StringBuilder(Regex.Replace(sb.ToString(), "\\s+", " "));
|
||||||
|
|
||||||
|
// Remove <head> and <script> sections
|
||||||
|
sb = new StringBuilder(Regex.Replace(sb.ToString(), "<head.*?</head>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline));
|
||||||
|
sb = new StringBuilder(Regex.Replace(sb.ToString(), "<script.*?</script>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline));
|
||||||
|
|
||||||
|
// Replace HTML entities
|
||||||
|
string[] oldWords = { " ", "&", """, "<", ">", "®", "©", "•", "™", "'" };
|
||||||
|
string[] newWords = { " ", "&", "\"", "<", ">", "®", "©", "•", "™", "'" };
|
||||||
|
for (int i = 0; i < oldWords.Length; i++) sb.Replace(oldWords[i], newWords[i]);
|
||||||
|
|
||||||
|
// Handle line breaks
|
||||||
|
sb.Replace("<br>", "\n").Replace("<br ", "\n<br ").Replace("<p ", "\n<p ");
|
||||||
|
|
||||||
|
// Remove all HTML tags
|
||||||
|
var plainText = Regex.Replace(sb.ToString(), "<[^>]*>", "").Trim();
|
||||||
|
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToCamelCase(this string input) {
|
||||||
|
if (string.IsNullOrEmpty(input)) return input;
|
||||||
|
|
||||||
|
var words = input.Split(new[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
for (var i = 0; i < words.Length; i++) {
|
||||||
|
words[i] = i == 0 ? words[i].ToLower() : char.ToUpper(words[i][0]) + words[i][1..].ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join("", words);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/MaksIT.Core/MaksIT.Core.csproj
Normal file
26
src/MaksIT.Core/MaksIT.Core.csproj
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>MaksIT.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
|
||||||
|
|
||||||
|
<!-- NuGet package metadata -->
|
||||||
|
<PackageId>MaksIT.Core</PackageId>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<Authors>Maksym Sadovnychyy</Authors>
|
||||||
|
<Company>MAKS-IT</Company>
|
||||||
|
<Product>MaksIT.Core</Product>
|
||||||
|
<Description>MaksIT.Core is a collection of helper methods and extensions for .NET projects, designed to simplify common tasks and improve code readability. The library includes extensions for `Guid`, `string`, `Object`, and a base class for creating enumeration types.</Description>
|
||||||
|
<PackageTags>dotnet;enumeration;string;guid;object;parsers;extensions</PackageTags>
|
||||||
|
<RepositoryUrl>https://github.com/MAKS-IT-COM/maksit-core</RepositoryUrl>
|
||||||
|
<License>MIT</License>
|
||||||
|
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="../../README.md" Pack="true" PackagePath="" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
7
src/Release-NuGetPackage.bat
Normal file
7
src/Release-NuGetPackage.bat
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
REM Change directory to the location of the script
|
||||||
|
cd /d %~dp0
|
||||||
|
|
||||||
|
REM Invoke the PowerShell script (Release-NuGetPackage.ps1) in the same directory
|
||||||
|
powershell -ExecutionPolicy Bypass -File "%~dp0Release-NuGetPackage.ps1"
|
||||||
46
src/Release-NuGetPackage.ps1
Normal file
46
src/Release-NuGetPackage.ps1
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Retrieve the API key from the environment variable
|
||||||
|
$apiKey = $env:NUGET_MAKS_IT
|
||||||
|
if (-not $apiKey) {
|
||||||
|
Write-Host "Error: API key not found in environment variable NUGET_MAKS_IT."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# NuGet source
|
||||||
|
$nugetSource = "https://api.nuget.org/v3/index.json"
|
||||||
|
|
||||||
|
# Define paths
|
||||||
|
$solutionDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$projectDir = "$solutionDir\MaksIT.Core"
|
||||||
|
$outputDir = "$projectDir\bin\Release"
|
||||||
|
|
||||||
|
# Clean previous builds
|
||||||
|
Write-Host "Cleaning previous builds..."
|
||||||
|
dotnet clean $projectDir -c Release
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
Write-Host "Building the project..."
|
||||||
|
dotnet build $projectDir -c Release
|
||||||
|
|
||||||
|
# Pack the NuGet package
|
||||||
|
Write-Host "Packing the project..."
|
||||||
|
dotnet pack $projectDir -c Release --no-build
|
||||||
|
|
||||||
|
# Look for the .nupkg file
|
||||||
|
$packageFile = Get-ChildItem -Path $outputDir -Filter "*.nupkg" -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
|
||||||
|
if ($packageFile) {
|
||||||
|
Write-Host "Package created successfully: $($packageFile.FullName)"
|
||||||
|
|
||||||
|
# Push the package to NuGet
|
||||||
|
Write-Host "Pushing the package to NuGet..."
|
||||||
|
dotnet nuget push $packageFile.FullName -k $apiKey -s $nugetSource --skip-duplicate
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "Package pushed successfully."
|
||||||
|
} else {
|
||||||
|
Write-Host "Failed to push the package."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "Package creation failed. No .nupkg file found."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
49
src/Release-NuGetPackage.sh
Normal file
49
src/Release-NuGetPackage.sh
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Retrieve the API key from the environment variable
|
||||||
|
apiKey=$NUGET_MAKS_IT
|
||||||
|
if [ -z "$apiKey" ]; then
|
||||||
|
echo "Error: API key not found in environment variable NUGET_MAKS_IT."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NuGet source
|
||||||
|
nugetSource="https://api.nuget.org/v3/index.json"
|
||||||
|
|
||||||
|
# Define paths
|
||||||
|
scriptDir=$(dirname "$0")
|
||||||
|
solutionDir=$(realpath "$scriptDir")
|
||||||
|
projectDir="$solutionDir/MaksIT.Core"
|
||||||
|
outputDir="$projectDir/bin/Release"
|
||||||
|
|
||||||
|
# Clean previous builds
|
||||||
|
echo "Cleaning previous builds..."
|
||||||
|
dotnet clean "$projectDir" -c Release
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
echo "Building the project..."
|
||||||
|
dotnet build "$projectDir" -c Release
|
||||||
|
|
||||||
|
# Pack the NuGet package
|
||||||
|
echo "Packing the project..."
|
||||||
|
dotnet pack "$projectDir" -c Release --no-build
|
||||||
|
|
||||||
|
# Look for the .nupkg file
|
||||||
|
packageFile=$(find "$outputDir" -name "*.nupkg" -print0 | xargs -0 ls -t | head -n 1)
|
||||||
|
|
||||||
|
if [ -n "$packageFile" ]; then
|
||||||
|
echo "Package created successfully: $packageFile"
|
||||||
|
|
||||||
|
# Push the package to NuGet
|
||||||
|
echo "Pushing the package to NuGet..."
|
||||||
|
dotnet nuget push "$packageFile" -k "$apiKey" -s "$nugetSource" --skip-duplicate
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Package pushed successfully."
|
||||||
|
else
|
||||||
|
echo "Failed to push the package."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Package creation failed. No .nupkg file found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Loading…
Reference in New Issue
Block a user