diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3721bae --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @framinosona diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b58d996 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,120 @@ +# GitHub Copilot Instructions for Plugin.BaseTypeExtensions + +## Project Overview + +**Plugin.BaseTypeExtensions** is a collection of extension methods for .NET base types — +strings, numerics, enums, collections, dates/times, comparables, reflection, and a handful of +other primitives — aimed at productivity, type safety, and readability in modern .NET +applications. + +**Key Features:** +- Null-safe string operations and character manipulation +- Generic numeric operations for all `INumber` types (angles, percentages, clamping) +- Enum flag manipulation, description retrieval, and general enum utilities +- Dictionary/list/enumerable extensions, including synchronization-aware variants +- `DateTime`/`DateOnly`/`TimeOnly`/`TimeSpan` utilities +- Reflection and assembly helpers (type discovery, attribute scanning, embedded resources) +- `IComparable` extensions plus standalone `ComparableTools`/`NumericRangeTools` helpers +- Smaller extensions for `Byte`, `Guid`, `Version`, `Uri`, `Stream`, `Task`, `TaskCompletionSource`, + `CancellationToken`, `SemaphoreSlim`, `Random`, `Exception` + +## Technology Stack + +- **Framework:** .NET 9.0 (SDK `9.0.305`, see `global.json`) +- **Language:** C# (latest) +- **Testing:** xUnit + FluentAssertions (v7.x — stays open source indefinitely per Directory.Packages.props comment) +- **Package Management:** Central Package Management (`Directory.Packages.props`) +- **CI/CD:** GitHub Actions +- **Documentation:** DocFX (`Plugin.BaseTypeExtensions.Docs/`, published to GitHub Pages) + +## Project Structure + +``` +Plugin.BaseTypeExtensions/ +├── .github/ +│ ├── workflows/ci.yml +│ └── copilot-instructions.md # This file +├── Plugin.BaseTypeExtensions/ # Main library — one file per extended type/category +│ ├── StringExtensions.cs +│ ├── NumericExtensions.cs / NumericRangeTools.cs +│ ├── EnumExtensions.cs +│ ├── DictionaryExtensions.cs / ListExtensions.cs / EnumerableExtensions.cs +│ ├── ConcurrentDictionaryExtensions.cs / SemaphoreSlimExtensions.cs +│ ├── DateTimeExtensions.cs / DateOnlyExtensions.cs / TimeOnlyExtensions.cs / TimeSpanExtensions.cs +│ ├── ComparableExtensions.cs / ComparableTools.cs +│ ├── ReflectionExtensions.cs / AssemblyExtensions.cs +│ ├── ByteExtensions.cs / GuidExtensions.cs / VersionExtensions.cs / UriExtensions.cs / StreamExtensions.cs +│ ├── TaskExtensions.cs / TaskCompletionSourceExtensions.cs / CancellationTokenExtensions.cs +│ ├── RandomExtensions.cs / ExceptionExtensions.cs +├── Plugin.BaseTypeExtensions.Tests/ # xUnit tests, one file per source file +└── Plugin.BaseTypeExtensions.Docs/ # DocFX documentation site +``` + +## Development Setup + +### Prerequisites +- .NET SDK 9.0.305 or later (see `global.json`) + +### Build Commands +```bash +dotnet restore Plugin.BaseTypeExtensions.slnx +dotnet build Plugin.BaseTypeExtensions.slnx --configuration Release --no-restore +dotnet test Plugin.BaseTypeExtensions.Tests/Plugin.BaseTypeExtensions.Tests.csproj --configuration Release --no-build +``` + +## Code Organization Patterns + +- **One file per extended type or tightly-related group** — `*Extensions.cs`, named after the + type it extends (e.g. `StringExtensions.cs`). Don't mix categories in one file. +- **Generic numeric code uses `INumber`** constraints instead of per-type overloads. +- **Standalone helpers that don't read naturally as extension methods** (e.g. clamping across a + pair of bounds) live in `ComparableTools`/`NumericRangeTools` as static classes — keep that + separation rather than bolting them onto `ComparableExtensions`/`NumericExtensions`. + +## Coding Standards + +### Naming Conventions (enforced by `.editorconfig`) +- Classes/structs/interfaces/methods/properties/constants: PascalCase +- Private fields: `_camelCase` +- Parameters/locals: camelCase + +### Style Guidelines +- Line length: max 240 characters; indentation: 4 spaces; line endings: LF +- Prefer `var` when the type is apparent +- Braces always required, even for single-line statements +- `System` usings first, separated from the rest + +### Documentation Requirements +- XML docs (``, ``, ``, ``) required on all public APIs + +## Testing Guidelines + +- **Framework:** xUnit, assertions via FluentAssertions (v7.2.2, open-source) +- **File naming:** `{ClassName}Tests.cs`, matching the source file +- **Test naming:** `MethodName_Scenario_ExpectedBehavior` +- Cover success paths, failure paths, and boundary conditions (null, empty, min/max values) +- Use `[Theory]`/`[InlineData]` for multiple similar cases; keep tests independent of each other + +## Dependencies + +### Production +- `Microsoft.Extensions.Logging.Abstractions` (10.0.11) +### Development +- `xunit` (2.9.3), `xunit.runner.visualstudio` (4.0.0), `FluentAssertions` (7.2.2) +- `coverlet.collector` / `coverlet.msbuild` (10.0.1) +- `Microsoft.NET.Test.Sdk` (18.9.0), `JetBrains.Annotations` (2025.2.0) +- `Microsoft.CodeAnalysis.NetAnalyzers` (10.0.400), `Microsoft.SourceLink.GitHub` (10.0.201) + +(Versions drift via Dependabot — check `Directory.Packages.props` for current values rather than +trusting this list long-term.) + +## CI/CD Pipeline + +`ci.yml` runs: version generation → build/test/package → publish test artifacts → DocFX build and +deploy to GitHub Pages → publish to NuGet.org (on `main`) → tag & GitHub release. + +## Useful Resources + +- **Repository:** https://github.com/laerdal/Plugin.BaseTypeExtensions +- **NuGet Package:** https://www.nuget.org/packages/Plugin.BaseTypeExtensions +- **Documentation:** https://laerdal.github.io/Plugin.BaseTypeExtensions/ diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..7e2fdb8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,50 @@ +## Summary + +Describe the change in 2-6 lines. + +## Why + +Explain the reason for this change. + +## Change Type + +- [ ] Feature +- [ ] Bug fix +- [ ] Refactor +- [ ] Docs only +- [ ] CI/build/tooling + +## Affected Areas + +- [ ] String / Numeric / Enum extensions +- [ ] Collection extensions (Dictionary / List / Enumerable / ConcurrentDictionary) +- [ ] Temporal extensions (DateTime / DateOnly / TimeOnly / TimeSpan) +- [ ] Reflection / Assembly extensions +- [ ] Comparison extensions +- [ ] Tests +- [ ] Docs + +## Behavior And Compatibility + +- [ ] Public API changed +- [ ] Exception behavior changed +- [ ] No externally visible behavior change + +If any box above is checked, describe impact: + +## Tests + +- [ ] Unit tests added/updated +- [ ] Not applicable (explain) + +## Documentation + +- [ ] README feature list updated +- [ ] XML docs added/updated +- [ ] Not applicable (explain) + +## Checklist + +- [ ] Commit messages follow Conventional Commits (`type(scope): subject`) +- [ ] CI passes +- [ ] Change is scoped to one logical unit of work diff --git a/.github/skills/code-review/SKILL.md b/.github/skills/code-review/SKILL.md new file mode 100644 index 0000000..c2a4bae --- /dev/null +++ b/.github/skills/code-review/SKILL.md @@ -0,0 +1,36 @@ +--- +name: code-review +description: Use for reviewing pull requests on Plugin.BaseTypeExtensions — checks file-per-category organization, generic numeric constraints, and null/boundary safety on new extension methods. +--- + +# Code Review — Plugin.BaseTypeExtensions + +## Checklist for new or changed extension methods + +- [ ] **One file per extended type/category**: a new extension method belongs in the + `*Extensions.cs` file named after the type it extends (e.g. a new `DateTime` helper goes in + `DateTimeExtensions.cs`, not bundled into an unrelated file). A PR mixing categories into + one file, or adding a same-named method to two files, is a real finding. +- [ ] **Generic numeric code uses `INumber`**: new numeric operations should use generic math + constraints rather than adding per-type overloads (`int`, `double`, `float`, ...) — flag + copy-pasted overloads as a sign the generic constraint was skipped. +- [ ] **Null/boundary safety**: string, collection, and comparable extensions handle `null` + input explicitly (per this library's "null-safe" positioning) rather than throwing an + unhandled `NullReferenceException`. +- [ ] **Standalone helpers stay standalone**: logic that doesn't read naturally as an extension + method (e.g. clamping across a pair of bounds) belongs in `ComparableTools`/ + `NumericRangeTools` as static helpers — don't bolt it onto `ComparableExtensions`/ + `NumericExtensions` as an awkward extension method just to keep the "everything is an + extension" pattern. +- [ ] **XML docs**: new public members have accurate ``/``/``/ + `` docs. +- [ ] **README feature list**: a new extension category should be reflected there. + +## What to flag as a real risk, not a nit + +- A new extension method that duplicates behavior already available in `System.Linq` or the + BCL without a clear reason (this library's value is filling *gaps*, not re-wrapping existing + APIs). +- Breaking changes to existing extension method signatures — this is a published NuGet package + consumed by other repos in this org (e.g. `Plugin.ExceptionListeners`), so a signature change + is a semver-relevant, not a routine tweak. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0a20c72 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,55 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a +harassment-free experience for everyone, regardless of age, body size, visible or invisible +disability, ethnicity, sex characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, +and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from + the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their + explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Maintainers are responsible for clarifying and enforcing standards of acceptable behavior and +will take appropriate and fair corrective action in response to any behavior deemed +inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces for this project, and also applies when +an individual is officially representing the project in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the +maintainer, [@framinosona](https://github.com/framinosona), via the contact details listed on +their GitHub profile. All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), +version 2.1, available at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2c862d1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + +## Branching + +Work off a short-lived feature branch cut from `main`. Open a PR early and mark it as a Draft +if it's still in progress. + +## Commit convention + +This repo uses [Conventional Commits](https://www.conventionalcommits.org/): `type(scope): subject`, +imperative mood, <= 72 characters. Common types: `feat`, `fix`, `docs`, `style`, `refactor`, +`perf`, `test`, `chore`. Scope is optional but recommended (e.g. the extension category touched). + +## Pull requests + +Fill out the PR template. Keep each PR scoped to one logical change. CI must pass before merge. + +## Code style + +Follow this repo's `.editorconfig`. Prefer clarity over cleverness; avoid unnecessary abstraction +— this is a firm preference, not a suggestion. Keep each extension category in its own +`*Extensions.cs` file named after the type it extends (e.g. `StringExtensions.cs`, +`DateTimeExtensions.cs`) — don't mix categories in one file. See +`.github/copilot-instructions.md` for the existing conventions. + +## Testing + +All new functionality needs xUnit tests in `Plugin.BaseTypeExtensions.Tests`, using +FluentAssertions for assertions. Test both success and failure paths and boundary conditions +(null, empty, min/max values). + +## Documentation + +Public APIs need XML doc comments (``, ``, ``, ``). If you +add a new extension category, update the README's feature list too. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d34dc2f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security vulnerabilities privately using +[GitHub's private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) +(Security tab → "Report a vulnerability") rather than opening a public issue. + +We'll acknowledge reports as soon as possible and coordinate disclosure once a fix is available. + +## Supported Versions + +Only the latest published release is actively supported.