diff --git a/CronCraft.Test/CronCraft.Test.csproj b/CronCraft.Test/CronCraft.Test.csproj index 0baef8e..d17bc89 100644 --- a/CronCraft.Test/CronCraft.Test.csproj +++ b/CronCraft.Test/CronCraft.Test.csproj @@ -9,6 +9,7 @@ + diff --git a/CronCraft.Test/ServiceCollectionExtensionsTest.cs b/CronCraft.Test/ServiceCollectionExtensionsTest.cs new file mode 100644 index 0000000..0701e87 --- /dev/null +++ b/CronCraft.Test/ServiceCollectionExtensionsTest.cs @@ -0,0 +1,53 @@ +using CronCraft.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace CronCraft.Test; + +[TestClass] +public sealed class ServiceCollectionExtensionsTest +{ + [TestMethod] + public void AddCronCraft_RegistersServiceAsSingleton() + { + var services = new ServiceCollection(); + services.AddCronCraft(); + + using var provider = services.BuildServiceProvider(); + + var first = provider.GetRequiredService(); + var second = provider.GetRequiredService(); + + Assert.AreSame(first, second); + } + + [TestMethod] + public void AddCronCraft_AppliesConfiguration() + { + var services = new ServiceCollection(); + services.AddCronCraft(settings => + { + settings.Language = "en"; + settings.DayNameFormat = "full"; + }); + + using var provider = services.BuildServiceProvider(); + + var options = provider.GetRequiredService>(); + var cronCraft = provider.GetRequiredService(); + + Assert.AreEqual("en", options.Value.Language); + Assert.AreEqual("full", options.Value.DayNameFormat); + Assert.AreEqual("Every Monday at 09:00 AM", cronCraft.Convert("0 9 * * 1")); + } + + [TestMethod] + public void AddCronCraft_ReturnsServiceCollectionForChaining() + { + var services = new ServiceCollection(); + + var result = services.AddCronCraft(); + + Assert.AreSame(services, result); + } +} diff --git a/CronCraft/CronCraft.csproj b/CronCraft/CronCraft.csproj index 41718cd..512f4b4 100644 --- a/CronCraft/CronCraft.csproj +++ b/CronCraft/CronCraft.csproj @@ -17,6 +17,7 @@ + diff --git a/CronCraft/Extensions/ServiceCollectionExtensions.cs b/CronCraft/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..3ae51c7 --- /dev/null +++ b/CronCraft/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +using CronCraft.Models; +using Microsoft.Extensions.DependencyInjection; + +namespace CronCraft.Extensions; + +public static class ServiceCollectionExtensions +{ + /// + /// Adds CronCraft and its configuration options to the service collection. + /// + /// The service collection to add CronCraft to. + /// + /// An optional delegate used to configure . + /// + /// The service collection so that additional calls can be chained. + public static IServiceCollection AddCronCraft( + this IServiceCollection services, + Action? configure = null) + { + ArgumentNullException.ThrowIfNull(services); + + services.AddOptions(); + + if (configure is not null) + services.Configure(configure); + + services.AddSingleton(); + + return services; + } +} diff --git a/CronCraft/README.md b/CronCraft/README.md index 7565597..c965a5c 100644 --- a/CronCraft/README.md +++ b/CronCraft/README.md @@ -11,7 +11,7 @@ CronCraft is designed for **clarity, speed, and minimal setup**. Whether you're building a **web app**, a **CLI tool**, a **Windows Service**, or a **background worker** — just create a `CronSettings` object, and you're good to go. -> ❝ No dependency injection. No appsettings. Just clean, functional cron parsing. ❞ +> ❝ No dependency injection required. No appsettings. Just clean, functional cron parsing. ❞ --- @@ -21,6 +21,7 @@ Whether you're building a **web app**, a **CLI tool**, a **Windows Service**, or - 🌍 Localized day-of-week formatting (`short`, `full`, `single`, or custom) - 🕘 Time zone adjustment (optional) - 🔧 Fully configurable via `CronSettings` object +- 💉 Optional dependency injection registration with `AddCronCraft` - 📦 Lightweight, zero-dependency core logic --- @@ -64,6 +65,30 @@ Console.WriteLine($"📖 Human Readable (Local TZ): {humanReadable}"); Console.ReadLine(); ``` +### Dependency Injection + +For ASP.NET Core and generic host applications, register CronCraft through +`IServiceCollection`: + +```csharp +using CronCraft.Extensions; + +builder.Services.AddCronCraft(settings => +{ + settings.Language = "en"; + settings.DayNameFormat = "short"; +}); +``` + +Then inject `CronCraftService` where it is needed: + +```csharp +public class MyScheduler(CronCraftService cronCraft) +{ + public string Describe(string expression) => cronCraft.Convert(expression); +} +``` + **Expected Output:** ``` diff --git a/README.md b/README.md index 95a5dc7..898deee 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ CronCraft is designed for **clarity, speed, and minimal setup**. Whether you're building a **web app**, a **CLI tool**, a **Windows Service**, or a **background worker** — just create a `CronSettings` object, and you're good to go. -> ❝ No dependency injection. No appsettings. Just clean, functional cron parsing. ❞ +> ❝ No dependency injection required. No appsettings. Just clean, functional cron parsing. ❞ --- @@ -21,6 +21,7 @@ Whether you're building a **web app**, a **CLI tool**, a **Windows Service**, or - 🌍 Localized day-of-week formatting (`short`, `full`, `single`, or custom) - 🕘 Time zone adjustment (optional) - 🔧 Fully configurable via `CronSettings` object +- 💉 Optional dependency injection registration with `AddCronCraft` - 📦 Lightweight, zero-dependency core logic --- @@ -64,6 +65,30 @@ Console.WriteLine($"📖 Human Readable (Local TZ): {humanReadable}"); Console.ReadLine(); ``` +### Dependency Injection + +For ASP.NET Core and generic host applications, register CronCraft through +`IServiceCollection`: + +```csharp +using CronCraft.Extensions; + +builder.Services.AddCronCraft(settings => +{ + settings.Language = "en"; + settings.DayNameFormat = "short"; +}); +``` + +Then inject `CronCraftService` where it is needed: + +```csharp +public class MyScheduler(CronCraftService cronCraft) +{ + public string Describe(string expression) => cronCraft.Convert(expression); +} +``` + **Expected Output:** ```