Skip to content
Open
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
57 changes: 57 additions & 0 deletions Spark.Library/Extensions/ListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Text.Json;

namespace Spark.Library.Extensions
{
public static class ListExtensions
{
/// <summary>
/// The method serializer object to json
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns>string</returns>
public static string ToJson<T>(this List<T> source)
{
if (source is null)
{
throw new ArgumentException("You need list");
}

return JsonSerializer.Serialize(source);
}

/// <summary>
/// The collapse method collapses a collection of List into a single, flat collection
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="anotherList"></param>
/// <returns>List</returns>
public static List<T> Collapse<T>(this List<List<T>> source)
{
if (source is null)
{
throw new ArgumentException("You need list");
}
return source
.Aggregate(new List<T>(), (x, y) => x.Concat(y)
.ToList());
}

/// <summary>
/// The diff method compares the List against another List. This method will return the values in the original List that are not present in the given List
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="anotherList"></param>
/// <returns>List</returns>
public static List<T> Diff<T>(this List<T> source, List<T> anotherList)
{
if (source is null || anotherList is null)
{
throw new ArgumentException("You need list");
}

return source.Except(anotherList).ToList();
}
}
}
38 changes: 38 additions & 0 deletions Spark.Library/Extensions/Queryable/PagedResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

namespace Spark.Library.Extensions.Queryable
{
public class PagedResponse<T> : ResponsePage<T>
{
public PagedResponse(T data, int page, int limit)
{
Page = page;
Limit = limit;
Data = data;
}

public int Page { get; set; }
public int Limit { get; set; }
public Uri? FirstPage { get; set; }
public Uri? LastPage { get; set; }
public int Total { get; set; }
public Uri? NextPage { get; set; }
public Uri? PreviousPage { get; set; }

public string LinksRazor()
{
bool hasNext = NextPage is not null;

bool hasPrevious = PreviousPage is not null && Page > 1;

string style = ".pagination a {\r\n color: black;\r\n float: left;\r\n padding: 8px 16px;\r\n text-decoration: none;\r\n transition: background-color .3s;\r\n border: 1px solid #ddd;\r\n}\r\n\r\n.pagination a.active {\r\n background-color: #4CAF50;\r\n color: white;\r\n border: 1px solid #4CAF50;\r\n}\r\n\r\n.pagination a:hover:not(.active) {background-color: #ddd;} .isDisabled {\r\n color: currentColor;\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n text-decoration: none; pointer-events: none;\r\n cursor: default; \r\n }";

string html = $"<style>{style}</style>" +
$"<div class='pagination' > " +
$"<a {(hasPrevious ? "" : "disabled class='isDisabled'")} href='{PreviousPage}'>❮</a>" +
$"<a {(hasNext ? "" : "disabled class='isDisabled'")} href='{NextPage}'>❯</a>" +
$"</div>";

return html;
}
}
}
93 changes: 93 additions & 0 deletions Spark.Library/Extensions/Queryable/QueryableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using System.Linq.Expressions;

namespace Spark.Library.Extensions.Queryable
{
public static class QueryableExtensions
{
/// <summary>
/// The paginate method automatically takes care of setting the query's "limit" and "offset" based on the current page being viewed by the user.
/// By default, the current page is detected by the value of the page query string argument on the HTTP request.
/// This value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator.
/// Case if you not to use HttpRequest and IHttpContextAccessor you won't create links as
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="request"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="page"></param>
/// <param name="limit"></param>
/// <returns>PagedResponse</returns>
public static PagedResponse<List<T>> Paginate<T>(this IQueryable<T> items, int page = 1, int limit = 15, HttpRequest? request = null, IHttpContextAccessor? httpContextAccessor = null)
{
page = page <= 1 ? 1 : page;

var total = items
.ToList()
.Count();

var listPaginated = items
.Skip((page - 1) * limit)
.Take(limit);

var respose = new PagedResponse<List<T>>(listPaginated.ToList(), page, limit);

var totalPages = total / (double)limit;

int roundedTotalPages = Convert.ToInt32(Math.Ceiling(totalPages));

if (request is not null && httpContextAccessor is not null)
{
var route = request.Path.Value!;

string url = string.Concat(httpContextAccessor?.HttpContext?.Request.Scheme, "://", httpContextAccessor?.HttpContext?.Request.Host.ToUriComponent());


respose.NextPage =
page >= 1 && page < roundedTotalPages
? GetPageUri(url,page + 1, limit, route)
: null;

respose.PreviousPage =
page - 1 >= 1 && page <= roundedTotalPages
? GetPageUri(url, page + 1, limit, route)
: null;

respose.FirstPage = GetPageUri(url, 1, limit, route);

respose.LastPage = GetPageUri(url, roundedTotalPages, limit, route);
}

respose.Total = total;

return respose;
}

/// <summary>
/// The when method will execute the given callback when the first argument given to the method evaluates to true.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="conditional"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static IQueryable<T> When<T>(this IQueryable<T> source, bool conditional, Expression<Func<T, bool>> predicate)
{
if (conditional)
{
return source.Where(predicate);
}
return source;
}


private static Uri GetPageUri(string baseUri, int page = 1, int limit = 15, string route = "/not-found")
{
var _enpointUri = new Uri(string.Concat(baseUri, route));
var modifiedUri = QueryHelpers.AddQueryString(_enpointUri.ToString(), "page", page.ToString());
modifiedUri = QueryHelpers.AddQueryString(modifiedUri, "limit", limit.ToString());
return new Uri(modifiedUri);
}
}
}
18 changes: 18 additions & 0 deletions Spark.Library/Extensions/Queryable/ResponsePage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Spark.Library.Extensions.Queryable
{
public class ResponsePage<T>
{

public ResponsePage()
{

}
public ResponsePage(T? data)
{
Data = data;
}


public T? Data { get; set; }
}
}
154 changes: 146 additions & 8 deletions Spark.Library/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
using DotNetEnv;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Spark.Library.Extensions;

public static class StringExtensions
{
public static string Clamp(this string value, int maxChars)

private static Dictionary<string, Dictionary<string, string>> _snakeCache = new Dictionary<string, Dictionary<string, string>>();

public static string Clamp(this string value, int maxChars,string end = "...")
{
return value.Length <= maxChars ? value : value.Substring(0, maxChars) + "...";
if (value != null && Encoding.UTF8.GetByteCount(value) <= maxChars)
{
return value;
}

if (value.Length <= maxChars)
{
return value;
}

if (value.Length >= maxChars)
{
return string.Concat(value.AsSpan(0, maxChars), end);
}

string truncated = value != null ? value.Substring(0, maxChars) : "";

if (truncated.Length > 0)
{
truncated = truncated.Remove(truncated.LastIndexOf(" ", StringComparison.Ordinal));
}

return $"{truncated}{end}";

}

public static string ToSlug(this string input)
Expand Down Expand Up @@ -71,4 +93,120 @@ public static string ToUpperFirst(this string value)
string restOfString = value[1..];
return firstChar + restOfString;
}


/// <summary>
/// The mask method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers:
/// </summary>
/// <param name="str"></param>
/// <param name="character"></param>
/// <param name="index"></param>
/// <param name="length"></param>
/// <param name="encoding"></param>
/// <returns>string</returns>
public static string Mask(this string str, string character, int index, int? length = null, string encoding = "UTF-8")
{
if (character == "")
{
return str;
}

string segment = length != null ? str.Substring(index, length.Value) : str.Substring(index);

if (segment == "")
{
return str;
}

int strlen = Encoding.GetEncoding(encoding).GetByteCount(str);
int startIndex = index;

if (index < 0)
{
startIndex = index < -strlen ? 0 : strlen + index;
}

string start = str.Substring(0, startIndex);
int segmentLen = Encoding.GetEncoding(encoding).GetByteCount(segment);
string end = str.Substring(startIndex + segmentLen);

return start + new string(character[0], segmentLen) + end;
}


/// <summary>
/// The snake method converts the given string to snake_case:
/// </summary>
/// <param name="value"></param>
/// <param name="delimiter"></param>
/// <returns>string</returns>
public static string Snake(this string value, string delimiter = "_")
{
string key = value;

if (_snakeCache.ContainsKey(key) && _snakeCache[key].ContainsKey(delimiter))
{
return _snakeCache[key][delimiter];
}

if (!string.IsNullOrEmpty(value) && !value.All(char.IsLower))
{
value = Regex.Replace(value, @"\s+", string.Empty);
value = Regex.Replace(value, @"(.)(?=[A-Z])", "$1" + delimiter);
value = value.ToLowerInvariant();
}

string result = value;

if (_snakeCache.ContainsKey(key))
{
_snakeCache[key].Add(delimiter, result);
}
else
{
_snakeCache.Add(key, new Dictionary<string, string> { { delimiter, result } });
}

return result;
}


/// <summary>
/// The method determines if the given string is valid JSON:
/// </summary>
/// <param name="value"></param>
/// <returns>bool</returns>
public static bool IsJson(this string value)
{
if (string.IsNullOrEmpty(value))
{
return false;
}

try
{
var result = JsonSerializer.Deserialize<dynamic>(value);
return true;
}
catch (JsonException)
{
return false;
}
}


/// <summary>
/// the method converts the given string to Title Case
/// </summary>
/// <param name="text"></param>
/// <returns>string</returns>
public static string Title(this string text)
{
string cultureName = CultureInfo.CurrentCulture.Name ?? "en-US";

var textinfo = new CultureInfo(cultureName, false).TextInfo;

return textinfo.ToTitleCase(text);
}

}
Loading