Skip to content

Refactor/cost speed #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Jun 28, 2025
Merged

Refactor/cost speed #23

merged 48 commits into from
Jun 28, 2025

Conversation

kooksee
Copy link
Collaborator

@kooksee kooksee commented Apr 10, 2025

This pull request introduces significant changes to the dependency injection (DI) framework, including the restructuring of internal packages, renaming and refactoring of files, and improvements to error handling and logging. The changes aim to simplify the codebase, improve maintainability, and enhance functionality. Below are the most important changes grouped by theme:

Package Restructuring and Refactoring

  • The dix_internal package was renamed to dixinternal, and all references were updated accordingly. This includes renaming files such as dix_internal/aaa.go to dixinternal/aaa.go and dix_internal/dix.go to dixinternal/dix.go. [1] [2]
  • Consolidated DI-related functionality into a new dixglobal package, which provides simplified APIs for registering and injecting dependencies.

Code Simplification

  • Removed redundant or unused methods and types from dix_internal (now dixinternal), such as dependency graph building, cycle detection, and utility functions. These functionalities were either refactored or replaced with more efficient implementations. [1] [2] [3] [4]
  • Simplified the Dix struct by replacing the node type with providerFn and refactoring methods like evalProvider to getOutputTypeValues for better clarity and performance. [1] [2]

Dependency Cycle Detection and Error Handling

  • Enhanced cycle detection by introducing a new implementation in dixinternal/cycle-check.go. This includes improved logging and assertion handling to prevent runtime errors caused by circular dependencies.
  • Added detailed error messages and structured logging for provider evaluation, making it easier to debug issues during dependency injection. [1] [2]

API Improvements

  • Updated the Provide and Inject methods in dixglobal to handle both struct and function types more robustly. These methods now include additional checks and logging for better usability.
  • Improved the Graph method to provide a clear representation of the dependency graph, making it easier to visualize the relationships between components.

Makefile Enhancements

  • Added a lint target to the Makefile for running golangci-lint with a timeout and verbose output, ensuring code quality and consistency.

Summary by CodeRabbit

  • New Features

    • Introduced a global dependency injection interface (dixglobal) for simplified usage.
    • Added DOT graph rendering for visualizing dependency graphs.
    • Added new example demonstrating error handling in dependency injection.
  • Bug Fixes

    • Improved cycle detection and error reporting for circular dependencies.
  • Refactor

    • Migrated internal package naming and structure for clarity and maintainability.
    • Enhanced provider registration and injection logic with stricter validation and better error handling.
    • Updated all examples to use the new global dependency injection interface.
  • Chores

    • Updated dependencies and Go version.
    • Added and updated linting and formatting configurations.
    • Improved Makefile with linting support.

kooksee and others added 30 commits February 24, 2025 18:34
@kooksee kooksee requested a review from Copilot June 24, 2025 01:55
Copilot

This comment was marked as outdated.

@kooksee kooksee requested a review from Copilot June 24, 2025 02:06
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the internal DI implementation by renaming packages, consolidating APIs under a new global package, and enhancing error handling, logging, and performance measurement. It also updates module requirements and adds linting support.

  • Renamed dix_internal to dixinternal and introduced dixglobal for simplified DI usage
  • Refactored provider/inject internals (providerFn, getOutputTypeValues, cycle detection) with improved logging and timing
  • Updated Go work setup, module dependencies, Makefile lint target, and CI lint action

Reviewed Changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated no comments.

Show a summary per file
File Description
go.work(.sum), go.mod Updated workspace and bumped module dependencies
example/** Switched imports from di to dixglobal
dixinternal/… Renamed package, rewrote provider/inject logic
dixglobal/global.go New global API for Provide, Inject, Graph
Makefile, .golangci.yml Added lint target and linter configuration
.github/workflows/lint.yml Upgraded actions and Go version constraint
Comments suppressed due to low confidence (5)

dixglobal/global.go:13

  • [nitpick] The example refers to di.New() but this package is dixglobal; update the comment to use dixglobal.New() for clarity.
//	c := di.New()

dixinternal/cycle-check.go:8

  • [nitpick] The new cycle detection logic in isCycle lacks unit tests; consider adding tests to verify detection and error reporting of circular dependencies.
func (x *Dix) isCycle() (string, bool) {

dixinternal/dix.go:27

  • Options has no Check method declared; this call will fail to compile. Consider defining a Check() method on Options or removing this call.
	option.Check()

Copy link

coderabbitai bot commented Jun 26, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This change refactors and modernizes the dependency injection (DI) system by replacing the old di and dix_internal packages with a new dixglobal package and an internal dixinternal implementation. The update introduces stricter type checks, improved error handling, enhanced graph rendering, and cycle detection. Examples are updated to use the new API, and the build system is modernized with new linting configurations and updated dependencies.

Changes

File(s) / Group Change Summary
.github/workflows/lint.yml, .golangci.yml, Makefile Updated lint workflow to use newer actions and Go version; added .golangci.yml with strict linter config; added lint target to Makefile.
go.mod, example/go.mod Updated Go version and dependencies; added new example module file.
di/di.go, dix_internal/* Deleted old DI facade and internal implementation files.
dix.go Updated internal import path and type references for new internal package.
dixinternal/aaa.go, dixinternal/option.go, dixinternal/dix.go Renamed package, updated comments, improved logger and option handling, stricter type validation, and enhanced provider registration.
dixinternal/api.go, dixinternal/cycle-check.go, dixinternal/provider.go, dixinternal/renderer.go, dixinternal/util.go Introduced new internal DI API, cycle detection, provider/function metadata, DOT graph rendering, and reflection utilities.
dixglobal/global.go Added new global DI facade with Provide/Inject/Graph API.
example/*/main.go Switched all example usages from di to dixglobal package.
example/test-return-error/main.go Added new example demonstrating error handling in DI providers and injectors.

Sequence Diagram(s)

Loading
sequenceDiagram
    participant User
    participant dixglobal
    participant dixinternal

    User->>dixglobal: Provide(constructor)
    dixglobal->>dixinternal: Provide(constructor)

    User->>dixglobal: Inject(target, opts...)
    dixglobal->>dixinternal: Inject(target, opts...)
    dixinternal->>dixinternal: Check for cycles
    alt No cycle
        dixinternal->>dixinternal: Inject dependencies
        dixinternal-->>dixglobal: Return injected target
    else Cycle detected
        dixinternal-->>dixglobal: Log error, abort
    end

    User->>dixglobal: Graph()
    dixglobal->>dixinternal: Graph()
    dixinternal-->>dixglobal: Return dependency graph

Poem

🐇
A carrot for the code, a hop for DI,
Old di is gone, let dixglobal fly!
With stricter checks and graphs that gleam,
Cycle-free bunnies chase their dream.
Linting sharp, the garden clean—
Dependency magic, crisp and keen!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87a0dfb and 128d8e5.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • example/go.mod (1 hunks)
  • example/test-return-error/main.go (1 hunks)
  • go.mod (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (1)
Makefile (1)

1-1: Consider adding standard phony targets to improve Makefile completeness.

While the lint target addition is good, consider adding standard phony targets that are commonly expected in Makefiles.

-.PHONY: test, lint
+.PHONY: all clean test lint

You may also want to add corresponding all and clean targets:

all: test lint

clean:
	go clean -cache -testcache
🧹 Nitpick comments (10)
.golangci.yml (1)

2-5: Consider enabling additional linters for better code quality.

The configuration only enables govet, which is quite conservative. Consider enabling additional linters like errcheck, staticcheck, unused, or ineffassign to catch more potential issues.

 linters:
   default: none
   enable:
     - govet
+    - errcheck
+    - staticcheck
+    - unused
+    - ineffassign
dixinternal/option.go (1)

18-20: ```shell
#!/bin/bash

Display the contents of dixinternal/option.go to locate the Options struct

sed -n '1,200p' dixinternal/option.go


</blockquote></details>
<details>
<summary>example/cycle/main.go (1)</summary><blockquote>

`35-35`: **Consider adding error handling demonstration.**

The explicit injection call will trigger cycle detection, but the example no longer demonstrates how applications might handle such errors gracefully. Consider wrapping this in a defer/recover block to show how cycle detection errors can be caught.


```diff
+defer func() {
+	if r := recover(); r != nil {
+		fmt.Printf("Caught cycle detection error: %v\n", r)
+	}
+}()
 dixglobal.Inject(func(*C) {})
dixglobal/global.go (1)

9-9: Document thread safety guarantees

The global singleton _dix is accessed by all exported functions. Please document whether the underlying dixinternal container is thread-safe for concurrent access.

dixinternal/renderer.go (1)

76-108: Use English for code comments

The DOT graph contains Chinese comments. For consistency and broader accessibility, please use English.

-	// 设置布局引擎
+	// Set layout engine
     layout=dot;
 
-    // 图形整体设置
-    rankdir=LR;          // 左到右布局
-    overlap=false;       // 避免节点重叠
-    splines=true;        // 使用曲线边
-    nodesep=0.5;         // 节点间距
-    ranksep=1.0;         // 层级间距
-    concentrate=true;    // 合并重复边
+    // Graph settings
+    rankdir=LR;          // Left to right layout
+    overlap=false;       // Avoid node overlap
+    splines=true;        // Use curved edges
+    nodesep=0.5;         // Node spacing
+    ranksep=1.0;         // Rank spacing
+    concentrate=true;    // Merge duplicate edges
 
-    // 节点样式
+    // Node style
     node [
         shape=box,
         style=filled,
@@ -99,7 +99,7 @@
         fixedsize=false
     ];
 
-    // 边样式
+    // Edge style
     edge [
         arrowhead=vee,
         arrowsize=0.4,
dixinternal/util.go (4)

12-15: Consider optimizing slice creation with proper capacity.

Pre-allocate the slice with the known capacity to avoid potential reallocations.

 func makeList(typ reflect.Type, data []reflect.Value) reflect.Value {
-	val := reflect.MakeSlice(reflect.SliceOf(typ), 0, 0)
+	val := reflect.MakeSlice(reflect.SliceOf(typ), 0, len(data))
 	return reflect.Append(val, data...)
 }

24-25: Clarify the comment about default value behavior.

The comment should indicate that it only applies when valueList is false.

-		// The last value as the default value
+		// When valueList is false, use the last value as the default value

164-164: Fix variable name typo.

-	switch inTye := typ; inTye.Kind() {
+	switch inTyp := typ; inTyp.Kind() {

Also update all references to inTye in this function to inTyp.


214-215: Translate Chinese comment to English for consistency.

-// isSupportedType 检查是否为支持的类型
+// isSupportedType checks if the type is supported
dixinternal/dix.go (1)

450-450: Fix comment to match the assertion logic.

The assertion allows up to 2 outputs, but the comment is confusing.

-	assert.If(typ.NumOut() > 2, "the func of provider output num should >= two")
+	assert.If(typ.NumOut() > 2, "the func of provider output num should be at most 2")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98ac5ce and 27b7f3d.

⛔ Files ignored due to path filters (4)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work is excluded by !**/*.work
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • .github/workflows/lint.yml (1 hunks)
  • .golangci.yml (1 hunks)
  • Makefile (1 hunks)
  • di/di.go (0 hunks)
  • dix.go (1 hunks)
  • dix_internal/api.go (0 hunks)
  • dix_internal/cycle-check.go (0 hunks)
  • dix_internal/graph.go (0 hunks)
  • dix_internal/node.go (0 hunks)
  • dix_internal/util.go (0 hunks)
  • dixglobal/global.go (1 hunks)
  • dixinternal/aaa.go (3 hunks)
  • dixinternal/api.go (1 hunks)
  • dixinternal/cycle-check.go (1 hunks)
  • dixinternal/dix.go (11 hunks)
  • dixinternal/option.go (2 hunks)
  • dixinternal/provider.go (1 hunks)
  • dixinternal/renderer.go (1 hunks)
  • dixinternal/util.go (1 hunks)
  • example/cycle/main.go (2 hunks)
  • example/func/main.go (2 hunks)
  • example/go.mod (1 hunks)
  • example/handler/main.go (2 hunks)
  • example/inject_method/main.go (2 hunks)
  • example/lazy/main.go (2 hunks)
  • example/list-nil/main.go (2 hunks)
  • example/list/main.go (2 hunks)
  • example/map-nil/main.go (2 hunks)
  • example/map/main.go (3 hunks)
  • example/struct-in/main.go (2 hunks)
  • example/struct-out/main.go (4 hunks)
  • example/test-return-error/main.go (1 hunks)
  • go.mod (1 hunks)
💤 Files with no reviewable changes (6)
  • dix_internal/api.go
  • dix_internal/cycle-check.go
  • di/di.go
  • dix_internal/node.go
  • dix_internal/util.go
  • dix_internal/graph.go
🧰 Additional context used
🧬 Code Graph Analysis (10)
example/map-nil/main.go (1)
dixglobal/global.go (2)
  • Graph (42-44)
  • Inject (31-39)
example/func/main.go (2)
dixglobal/global.go (3)
  • Provide (24-26)
  • Graph (42-44)
  • Inject (31-39)
dix.go (3)
  • Provide (39-41)
  • Graph (17-17)
  • Inject (28-37)
dix.go (4)
dixinternal/option.go (3)
  • Option (4-4)
  • Options (5-8)
  • WithValuesNull (22-26)
dixinternal/dix.go (1)
  • Dix (43-48)
dixinternal/aaa.go (1)
  • Graph (23-26)
dixinternal/api.go (1)
  • New (11-13)
example/inject_method/main.go (2)
dixglobal/global.go (2)
  • Provide (24-26)
  • Inject (31-39)
dix.go (2)
  • Provide (39-41)
  • Inject (28-37)
example/map/main.go (2)
dixglobal/global.go (3)
  • Graph (42-44)
  • Provide (24-26)
  • Inject (31-39)
dix.go (3)
  • Graph (17-17)
  • Provide (39-41)
  • Inject (28-37)
example/list-nil/main.go (2)
dixglobal/global.go (2)
  • Graph (42-44)
  • Inject (31-39)
dix.go (2)
  • Graph (17-17)
  • Inject (28-37)
dixinternal/cycle-check.go (2)
dix.go (1)
  • Dix (16-16)
dixinternal/dix.go (1)
  • Dix (43-48)
example/cycle/main.go (3)
dixglobal/global.go (3)
  • Graph (42-44)
  • Provide (24-26)
  • Inject (31-39)
dix.go (3)
  • Graph (17-17)
  • Provide (39-41)
  • Inject (28-37)
example/struct-out/main.go (3)
  • B (43-45)
  • A (39-41)
  • C (20-20)
dixinternal/api.go (4)
dix.go (6)
  • New (24-26)
  • Option (14-14)
  • Dix (16-16)
  • Provide (39-41)
  • Inject (28-37)
  • Graph (17-17)
dixinternal/option.go (1)
  • Option (4-4)
dixinternal/dix.go (1)
  • Dix (43-48)
dixinternal/aaa.go (1)
  • Graph (23-26)
dixinternal/renderer.go (1)
dixinternal/dix.go (1)
  • Dix (43-48)
🪛 checkmake (0.2.2)
Makefile

[warning] 1-1: Missing required phony target "all"

(minphony)


[warning] 1-1: Missing required phony target "clean"

(minphony)


[warning] 1-1: Missing required phony target "test"

(minphony)

🔇 Additional comments (49)
.golangci.yml (1)

19-22: Formatter configuration looks good.

The enabled formatters (goimports and gofmt) are appropriate choices for maintaining consistent Go code formatting and import organization.

example/func/main.go (2)

6-6: Import migration completed correctly.

The switch from di to dixglobal package is consistent with the PR's refactoring objectives.


14-44: Function calls updated consistently.

All dependency injection calls have been properly migrated from di to dixglobal while maintaining the same functionality and logic flow.

example/struct-out/main.go (2)

6-6: Import migration implemented correctly.

The transition from di to dixglobal package aligns with the dependency injection framework refactoring.


49-97: All DI function calls updated consistently.

The migration maintains the complex dependency injection patterns while switching to the new dixglobal package. Error handling and graph visualization functionality are preserved.

example/inject_method/main.go (2)

6-6: Package import updated appropriately.

The migration from di to dixglobal is consistent with the framework refactoring goals.


32-40: Method injection example migrated successfully.

The dependency injection calls have been updated to use dixglobal while preserving the method injection demonstration and provider registration logic.

Makefile (1)

5-6: Lint target implementation is well-configured.

The lint target uses appropriate timeout and verbose settings for golangci-lint, which will provide comprehensive feedback during development.

.github/workflows/lint.yml (2)

17-17: Verify Go version requirement compatibility.

The Go version requirement changed from a fixed "1.19.0" to ">=1.22.0", which is a significant jump of 3 major versions. Ensure your codebase is compatible with Go 1.22+ features and that this doesn't break existing functionality.

#!/bin/bash
# Description: Check if the codebase uses Go 1.22+ specific features or if there are compatibility issues

# Check go.mod for current Go version requirement
if [ -f "go.mod" ]; then
    echo "Current go.mod version requirement:"
    grep "^go " go.mod
else
    echo "No go.mod found"
fi

# Check for potential Go version-specific features
echo "Checking for Go 1.22+ specific features..."
rg -n "range.*func|for.*range.*func" --type go || echo "No Go 1.22 range-over-func patterns found"

14-15: Verify compatibility of updated GitHub Actions versions.

The actions have been updated with significant version jumps (checkout v3→v4, setup-go v3→v5). Ensure these newer versions are stable and compatible with your workflow requirements.

Are GitHub Actions checkout@v4, setup-go@v5, and golangci-lint-action@v8 stable and widely adopted?
example/map-nil/main.go (2)

6-6: Migration to dixglobal package looks correct.

The import update from di to dixglobal is consistent with the broader refactoring effort and maintains the same API interface.


15-15: Function call migrations are consistent and correct.

All di.Graph() and di.Inject() calls have been properly updated to use dixglobal.Graph() and dixglobal.Inject() respectively, maintaining the same functionality with the new package structure.

Also applies to: 18-18, 25-25

example/list/main.go (2)

6-6: Import migration correctly implemented.

The package import has been properly updated from di to dixglobal as part of the refactoring effort.


13-13: All DI function calls successfully migrated.

All dependency injection function calls (Provide, Inject, Graph) have been consistently updated to use the new dixglobal package, maintaining the same functionality and API contract.

Also applies to: 18-18, 26-26, 34-34, 40-40, 42-42, 55-55, 62-62

dixinternal/option.go (2)

1-1: Package rename is consistent with the refactoring.

The package name change from dix_internal to dixinternal aligns with the broader restructuring effort and follows Go naming conventions better.


6-6: Comment translation improves code maintainability.

Translating the comment from Chinese to English improves accessibility for international contributors and follows common open-source practices.

example/struct-in/main.go (3)

7-7: Import addition for dixglobal is correct.

The new import for dixglobal package has been properly added alongside the existing dix import.


28-28: DI function calls properly migrated to dixglobal.

All dependency injection operations (Provide, Inject, Graph) have been consistently updated to use the new dixglobal package API.

Also applies to: 32-32, 36-36, 38-38, 43-43


38-38: Verify mixed package usage is intentional.

This function uses both dixglobal.Provide and references to *dix.Dix types in the same function. Ensure this mixed usage is intentional and that the two packages are designed to work together.

#!/bin/bash
# Description: Check if mixed usage of dix and dixglobal is common in examples

# Search for other examples that mix both packages
rg -l "github.com/pubgo/dix\"" example/
rg -l "dixglobal" example/
dix.go (3)

6-6: Clean package refactoring - import path updated correctly.

The import path change from dix_internal to dixinternal aligns with the package restructuring objectives.


10-17: Type aliases updated consistently with new package name.

All type aliases (Option, Options, Dix, Graph) and the constant InjectMethodPrefix are correctly updated to reference the new dixinternal package.


21-25: Function calls updated consistently with new package references.

Both WithValuesNull() and New() function calls are correctly updated to use the new dixinternal package while maintaining the same API.

example/map/main.go (2)

6-6: Clean migration to new global DI package.

The import change from di to dixglobal aligns with the new global dependency injection interface mentioned in the PR objectives.


15-15: All DI function calls updated consistently.

All calls to Provide, Inject, and Graph functions are correctly updated to use the new dixglobal package while maintaining the same functionality and API.

Also applies to: 18-18, 25-25, 31-31, 41-42

example/list-nil/main.go (2)

6-6: Consistent migration to dixglobal package.

The import change follows the same pattern as other examples, moving from the deprecated di package to the new dixglobal interface.


14-14: All DI operations updated consistently across the file.

All Inject and Graph function calls are properly updated to use the new dixglobal package, maintaining the same program logic and behavior.

Also applies to: 19-19, 30-30, 36-36, 43-43

example/lazy/main.go (2)

6-6: Import migration consistent with other examples.

The change from di to dixglobal follows the same pattern established in other example files.


15-15: All DI function calls updated consistently.

All Provide and Inject calls are correctly updated to use the new dixglobal package, preserving the lazy initialization demonstration logic.

Also applies to: 20-20, 25-25, 29-29

dixinternal/aaa.go (3)

1-1: Package name corrected to match new naming convention.

The package name change from dix_internal to dixinternal is consistent with the overall refactoring effort.


10-14: Comments improved for better clarity and accessibility.

The comments for defaultKey and InjectMethodPrefix have been translated from Chinese to English and rephrased for better clarity.


30-32: API improvement: SetLog function provides more flexibility.

The replacement of SetLogLevel with SetLog is a breaking change but provides much more flexibility by allowing custom logger configuration through a setter function. This aligns with the modernization goals mentioned in the PR objectives.

example/handler/main.go (1)

8-8: Clean package migration from di to dixglobal.

The migration is consistent and complete - all references to the old di package have been properly updated to use the new dixglobal package while maintaining identical functionality.

Also applies to: 26-26, 29-29, 33-33, 41-41, 48-48, 50-50, 56-56, 60-60

dixinternal/cycle-check.go (1)

7-24: ```shell
#!/bin/bash
grep -R "func buildDependencyGraph" -n dixinternal/util.go
grep -R "func detectCycle" -n dixinternal/util.go


</details>
<details>
<summary>example/cycle/main.go (1)</summary>

`6-6`: **Package migration completed successfully.**

The migration from `di` to `dixglobal` is clean and the circular dependency setup (A->B->C->A) is correctly maintained. The new cycle detection will now be handled internally by the DI framework.



Also applies to: 13-13, 24-35

</details>
<details>
<summary>dixinternal/api.go (3)</summary>

`10-13`: **Clean constructor implementation.**

The constructor properly delegates to the internal `newDix` function with options support.

---

`15-17`: **Simple provider registration.**

The `Provide` method correctly delegates to the internal implementation.

---

`32-37`: **Graph visualization implementation looks correct.**

The method properly constructs a Graph struct with both object and provider representations.

</details>
<details>
<summary>go.mod (1)</summary>

`3-3`: **Verify Go version 1.23.0 compatibility**

The specified Go version 1.23.0 appears to be a future version. Please ensure this is intentional and that your build environment supports it.

```web
What is the latest stable version of Go programming language?
example/test-return-error/main.go (1)

8-11: Use dixglobal instead of creating new instances

According to the PR objectives, examples should be updated to use the new dixglobal package instead of creating new DI instances.

-import (
-	"fmt"
-	"log"
-	"strings"
-
-	"github.com/pubgo/dix"
-	"github.com/pubgo/funk/recovery"
-	"github.com/pubgo/funk/try"
-)
+import (
+	"fmt"
+	"log"
+	"strings"
+
+	"github.com/pubgo/dix/dixglobal"
+	"github.com/pubgo/funk/recovery"
+	"github.com/pubgo/funk/try"
+)

Likely an incorrect or invalid review comment.

dixinternal/provider.go (1)

51-65: Excellent error handling and debugging context

The call method provides comprehensive error context including stack traces, input/output types, and actual values. This will greatly aid in debugging dependency injection issues.

dixinternal/util.go (3)

43-121: Well-structured output handling with proper validation.

The implementation correctly handles different output types with appropriate nil/zero value checks and recursive struct processing.


123-160: Correct implementation of cycle detection algorithm.

The DFS-based approach with proper visited tracking and path cloning ensures accurate cycle detection without side effects.


194-212: Efficient dependency graph construction.

Good use of pre-allocated map capacity to reduce rehashing overhead.

dixinternal/dix.go (6)

54-141: Excellent improvements to provider evaluation.

The enhanced type validation, detailed logging, and proper error handling for provider functions significantly improve the robustness of the dependency injection system.


152-156: Good addition of struct type handling.

Creating new struct instances and injecting dependencies provides better support for struct-based dependency injection.


225-276: Robust function injection with proper error handling.

The validation ensuring functions only return error types and the subsequent error checking provide a safer injection mechanism.


309-316: Improved error handling in inject method.

Returning errors instead of panicking provides a better API that allows callers to handle injection failures gracefully.


359-405: Comprehensive provider handling with proper validations.

The support for error-returning providers and validation of exported fields improves the reliability of the dependency injection system.


27-29: Verify that renaming Check() to Validate() doesn't break existing code.

The method rename from Check() to Validate() could be a breaking change if this was part of the public API.

#!/bin/bash
# Check if Check() method is still used elsewhere in the codebase
rg -A 3 "\.Check\(\)" --type go

Comment on lines 19 to 30
func (x *Dix) Inject(param any, opts ...Option) any {
if dep, ok := x.isCycle(); ok {
logger.Error().
Str("cycle_path", dep).
Str("component", reflect.TypeOf(param).String()).
Msg("dependency cycle detected")
assert.Must(errors.New("circular dependency: " + dep))
}

assert.Must(x.inject(param, opts...))
return param
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making cycle detection less aggressive.

The cycle detection logic is well-implemented with structured logging, but using assert.Must will cause the application to panic/exit on any circular dependency. Consider returning an error instead to allow applications to handle cycles gracefully.

 func (x *Dix) Inject(param any, opts ...Option) any {
 	if dep, ok := x.isCycle(); ok {
 		logger.Error().
 			Str("cycle_path", dep).
 			Str("component", reflect.TypeOf(param).String()).
 			Msg("dependency cycle detected")
-		assert.Must(errors.New("circular dependency: " + dep))
+		return errors.New("circular dependency: " + dep)
 	}
 
-	assert.Must(x.inject(param, opts...))
-	return param
+	if err := x.inject(param, opts...); err != nil {
+		return err
+	}
+	return param
 }
🤖 Prompt for AI Agents
In dixinternal/api.go around lines 19 to 30, the cycle detection currently uses
assert.Must which panics on detecting a circular dependency. Modify the Inject
method to return an error instead of panicking, so that the caller can handle
the circular dependency gracefully. Change the function signature to return
(any, error), replace assert.Must calls with error returns, and propagate errors
from the inject call accordingly.

Comment on lines +31 to +39
func Inject[T any](data T, opts ...dixinternal.Option) T {
vp := reflect.ValueOf(data)
if vp.Kind() == reflect.Struct {
_ = _dix.Inject(&data, opts...)
} else {
_ = _dix.Inject(data, opts...)
}
return data
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Injection errors are silently ignored

The Inject function discards errors returned by _dix.Inject(). This could hide important runtime errors such as missing dependencies or cyclic dependencies.

Consider either:

  1. Propagating the error by changing the signature to Inject[T any](data T, opts ...dixinternal.Option) (T, error)
  2. Panicking on error to maintain the current API while ensuring errors are visible
 func Inject[T any](data T, opts ...dixinternal.Option) T {
 	vp := reflect.ValueOf(data)
+	var err error
 	if vp.Kind() == reflect.Struct {
-		_ = _dix.Inject(&data, opts...)
+		err = _dix.Inject(&data, opts...)
 	} else {
-		_ = _dix.Inject(data, opts...)
+		err = _dix.Inject(data, opts...)
 	}
+	if err != nil {
+		panic(err)
+	}
 	return data
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func Inject[T any](data T, opts ...dixinternal.Option) T {
vp := reflect.ValueOf(data)
if vp.Kind() == reflect.Struct {
_ = _dix.Inject(&data, opts...)
} else {
_ = _dix.Inject(data, opts...)
}
return data
}
func Inject[T any](data T, opts ...dixinternal.Option) T {
vp := reflect.ValueOf(data)
var err error
if vp.Kind() == reflect.Struct {
err = _dix.Inject(&data, opts...)
} else {
err = _dix.Inject(data, opts...)
}
if err != nil {
panic(err)
}
return data
}
🤖 Prompt for AI Agents
In dixglobal/global.go around lines 31 to 39, the Inject function ignores errors
returned by _dix.Inject, which can hide critical runtime issues. Modify the
function signature to return both the injected data and an error, then capture
and return the error from _dix.Inject instead of discarding it. Update all call
sites accordingly to handle the error, ensuring injection failures are properly
reported and not silently ignored.

Comment on lines +13 to +24
func testok() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})

di.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update to use dixglobal API

Replace the local DI instance with the global API.

 func testok() {
-	di := dix.New(dix.WithValuesNull())
-	di.Provide(func() (*log.Logger, error) {
+	dixglobal.Provide(func() (*log.Logger, error) {
 		log.Println("provider ok")
 		return new(log.Logger), nil
 	})
 
-	di.Inject(func(l *log.Logger) error {
+	dixglobal.Inject(func(l *log.Logger) error {
 		log.Println("inject ok")
 		return nil
 	})
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func testok() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})
di.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
}
func testok() {
dixglobal.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})
dixglobal.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
}
🤖 Prompt for AI Agents
In example/test-return-error/main.go around lines 13 to 24, the code uses a
local DI instance created with dix.New, but the review requests replacing this
with the global dixglobal API. Update the code to remove the local DI instance
and instead use the global dixglobal methods for Provide and Inject, ensuring
the same providers and injectors are registered and called via dixglobal.

Comment on lines +26 to +44
func testInjectErr() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})

err := try.Try(func() error {
di.Inject(func(l *log.Logger) error {
return fmt.Errorf("inject_err")
})
return nil
})
if err != nil && strings.Contains(err.Error(), "inject_err") {
return
} else {
panic(err)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update to use dixglobal API

Replace the local DI instance with the global API.

 func testInjectErr() {
-	di := dix.New(dix.WithValuesNull())
-	di.Provide(func() (*log.Logger, error) {
+	dixglobal.Provide(func() (*log.Logger, error) {
 		log.Println("provider ok")
 		return new(log.Logger), nil
 	})
 
 	err := try.Try(func() error {
-		di.Inject(func(l *log.Logger) error {
+		dixglobal.Inject(func(l *log.Logger) error {
 			return fmt.Errorf("inject_err")
 		})
 		return nil
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func testInjectErr() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})
err := try.Try(func() error {
di.Inject(func(l *log.Logger) error {
return fmt.Errorf("inject_err")
})
return nil
})
if err != nil && strings.Contains(err.Error(), "inject_err") {
return
} else {
panic(err)
}
}
func testInjectErr() {
dixglobal.Provide(func() (*log.Logger, error) {
log.Println("provider ok")
return new(log.Logger), nil
})
err := try.Try(func() error {
dixglobal.Inject(func(l *log.Logger) error {
return fmt.Errorf("inject_err")
})
return nil
})
if err != nil && strings.Contains(err.Error(), "inject_err") {
return
} else {
panic(err)
}
}
🤖 Prompt for AI Agents
In example/test-return-error/main.go lines 26 to 44, the code uses a local DI
instance created with dix.New, but the review requests switching to the global
dixglobal API. Replace the local DI instance and its method calls with the
equivalent dixglobal API calls to use the global dependency injection container
instead of a local one.

Comment on lines 46 to 64
func testProviderErr() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
return nil, fmt.Errorf("provider_err")
})

err := try.Try(func() error {
di.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
return nil
})
if err != nil && strings.Contains(err.Error(), "provider_err") {
return
} else {
panic(err)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update to use dixglobal API

Replace the local DI instance with the global API.

 func testProviderErr() {
-	di := dix.New(dix.WithValuesNull())
-	di.Provide(func() (*log.Logger, error) {
+	dixglobal.Provide(func() (*log.Logger, error) {
 		return nil, fmt.Errorf("provider_err")
 	})
 
 	err := try.Try(func() error {
-		di.Inject(func(l *log.Logger) error {
+		dixglobal.Inject(func(l *log.Logger) error {
 			log.Println("inject ok")
 			return nil
 		})
 		return nil
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func testProviderErr() {
di := dix.New(dix.WithValuesNull())
di.Provide(func() (*log.Logger, error) {
return nil, fmt.Errorf("provider_err")
})
err := try.Try(func() error {
di.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
return nil
})
if err != nil && strings.Contains(err.Error(), "provider_err") {
return
} else {
panic(err)
}
}
func testProviderErr() {
dixglobal.Provide(func() (*log.Logger, error) {
return nil, fmt.Errorf("provider_err")
})
err := try.Try(func() error {
dixglobal.Inject(func(l *log.Logger) error {
log.Println("inject ok")
return nil
})
return nil
})
if err != nil && strings.Contains(err.Error(), "provider_err") {
return
} else {
panic(err)
}
}
🤖 Prompt for AI Agents
In example/test-return-error/main.go lines 46 to 64, the code uses a local DI
instance created with dix.New, but it should use the dixglobal API instead.
Replace the local DI instance creation and usage with calls to dixglobal's
global API methods to provide and inject dependencies, ensuring the code uses
the global DI container as intended.

Comment on lines +45 to +48
func (d *DotRenderer) EndSubgraph() {
d.indent = d.indent[:len(d.indent)-1]
d.writef("}")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential index out of bounds in EndSubgraph

The indent string manipulation could cause a panic if EndSubgraph is called when indent is empty.

 func (d *DotRenderer) EndSubgraph() {
+	if len(d.indent) > 0 {
 		d.indent = d.indent[:len(d.indent)-1]
+	}
 	d.writef("}")
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (d *DotRenderer) EndSubgraph() {
d.indent = d.indent[:len(d.indent)-1]
d.writef("}")
}
func (d *DotRenderer) EndSubgraph() {
if len(d.indent) > 0 {
d.indent = d.indent[:len(d.indent)-1]
}
d.writef("}")
}
🤖 Prompt for AI Agents
In dixinternal/renderer.go around lines 45 to 48, the EndSubgraph method slices
the indent string without checking if it is empty, which can cause an index out
of bounds panic. Add a check to ensure that indent is not empty before slicing
it to remove the last element. This prevents panics by only modifying indent
when it contains elements.

Comment on lines +173 to +176
inTyp := inTye.Field(j).Type
if !isSupportedType(typ) {
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Fix incorrect variable in type check.

The function is checking the wrong variable - it should check inTyp instead of typ.

 			inTyp := inTye.Field(j).Type
-			if !isSupportedType(typ) {
+			if !isSupportedType(inTyp) {
 				continue
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
inTyp := inTye.Field(j).Type
if !isSupportedType(typ) {
continue
}
inTyp := inTye.Field(j).Type
if !isSupportedType(inTyp) {
continue
}
🤖 Prompt for AI Agents
In dixinternal/util.go around lines 173 to 176, the code incorrectly checks the
variable 'typ' instead of 'inTyp' in the type check. Update the condition to
check 'inTyp' by replacing 'typ' with 'inTyp' in the isSupportedType function
call to ensure the correct variable is evaluated.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27b7f3d and 8bd5cf0.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work is excluded by !**/*.work
📒 Files selected for processing (3)
  • dixinternal/dix.go (12 hunks)
  • example/go.mod (1 hunks)
  • go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/go.mod
  • go.mod
🔇 Additional comments (10)
dixinternal/dix.go (10)

1-1: Package name change looks good.

The package rename from dix_internal to dixinternal follows Go naming conventions better by avoiding underscores.


86-99: Enhanced logging and timing measurement improve observability.

The addition of detailed provider execution logging with timing information and stack traces will greatly help with debugging dependency injection issues.


100-107: Provider error handling enhancement is well implemented.

The new error detection and panic behavior for provider functions that return errors provides much better error reporting with stack trace context.


268-275: Error handling for injected functions is well implemented.

The validation and handling of error return values from injected functions provides good error propagation with proper context wrapping.


309-316: Inject method improvement from panic to error return is excellent.

Changing the inject method to return an error instead of panicking makes the API much more user-friendly and allows for better error handling by callers.


360-373: Provider function validation enhancement improves type safety.

The strict validation of provider function signatures, especially the requirement that the second return value must be an error type, provides better type safety and clearer API contracts.


427-461: Provider registration validation is comprehensive and well-designed.

The enhanced validation for provider functions, including checks for variadic functions, output count limits, and proper signature validation, significantly improves the robustness of the dependency injection system.


390-402: ```shell
#!/bin/bash
grep '^go ' go.mod


---

`27-29`: ```shell
#!/bin/bash
# Locate Validate calls in dixinternal/dix.go
rg -n -F "Validate(" dixinternal/dix.go

152-156: ```shell
#!/bin/bash

Extract getValue and injectStruct implementations to confirm handling of struct vs pointer

rg -F 'func (x *Dix) getValue' -A 30 -B 5 dixinternal/dix.go
rg -F 'func (x *Dix) injectStruct' -A 30 -B 5 dixinternal/dix.go


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines 225 to 239
func (x *Dix) injectFunc(vp reflect.Value, opt Options) {
var inTypes []*inType
assert.If(vp.Type().NumOut() > 1, "func output num should <=1")
assert.If(vp.Type().NumIn() == 0, "func input num should not be zero")
var hasErrorReturn bool
if vp.Type().NumOut() == 1 {
// 如果有一个返回值,必须是 error 类型
errorType := vp.Type().Out(0)
if !errorType.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
panic(&errors.Err{
Msg: "injectable function can only return error type",
Detail: fmt.Sprintf("return_type=%s", errorType.String()),
})
}
hasErrorReturn = true
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function injection validation is overly strict and may break existing code.

The new validation requires injectable functions to either have no return value or return only an error type. This is a significant breaking change that may not be backward compatible.

Consider making this validation configurable or providing a migration path:

+func (x *Dix) injectFunc(vp reflect.Value, opt Options) {
+	// Allow legacy behavior through options
+	if !opt.StrictFunctionValidation {
+		// Original less strict validation
+		assert.If(vp.Type().NumOut() > 2, "func output num should <=2")
+		assert.If(vp.Type().NumIn() == 0, "func input num should not be zero")
+		// ... rest of original logic
+		return
+	}
+	
+	// Strict validation for new code
 	assert.If(vp.Type().NumOut() > 1, "func output num should <=1")

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In dixinternal/dix.go around lines 225 to 239, the current validation enforces
that injectable functions must have zero or one return value and if one, it must
be an error type, which is too restrictive and breaks backward compatibility.
Modify the validation to either make this check optional via a configuration
flag in Options or relax the constraint to allow other return types, ensuring
existing code continues to work. Provide a clear way to opt-in to strict
validation or a migration path for users to adapt.

Comment on lines 54 to 62
func (x *Dix) getOutputTypeValues(outTyp outputType, opt Options) map[group][]value {
switch outTyp.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Func:
default:
assert.Must(errors.Err{
Msg: "provider type kind error, the supported type kinds are <ptr,interface,func>",
Detail: fmt.Sprintf("type=%s kind=%s", typ, typ.Kind()),
Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Type kind validation is overly restrictive.

The assertion only allows ptr, interface, and func kinds, but the error message and later code suggest struct should also be supported. This will cause panics for valid struct types.

-	switch outTyp.Kind() {
-	case reflect.Ptr, reflect.Interface, reflect.Func:
-	default:
-		assert.Must(errors.Err{
-			Msg:    "provider type kind error, the supported type kinds are <ptr,interface,func>",
-			Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
-		})
-	}
+	switch outTyp.Kind() {
+	case reflect.Ptr, reflect.Interface, reflect.Func, reflect.Struct:
+	default:
+		assert.Must(errors.Err{
+			Msg:    "provider type kind error, the supported type kinds are <ptr,interface,func,struct>",
+			Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
+		})
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (x *Dix) getOutputTypeValues(outTyp outputType, opt Options) map[group][]value {
switch outTyp.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Func:
default:
assert.Must(errors.Err{
Msg: "provider type kind error, the supported type kinds are <ptr,interface,func>",
Detail: fmt.Sprintf("type=%s kind=%s", typ, typ.Kind()),
Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
})
}
func (x *Dix) getOutputTypeValues(outTyp outputType, opt Options) map[group][]value {
switch outTyp.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Func, reflect.Struct:
default:
assert.Must(errors.Err{
Msg: "provider type kind error, the supported type kinds are <ptr,interface,func,struct>",
Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
})
}
}
🤖 Prompt for AI Agents
In dixinternal/dix.go around lines 54 to 62, the type kind validation restricts
allowed kinds to ptr, interface, and func, but struct types should also be
supported according to the error message and later code. Update the switch case
to include reflect.Struct as an allowed kind to prevent panics for valid struct
types.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
dixinternal/dix.go (2)

56-63: Type kind validation is overly restrictive.

The assertion only allows ptr, interface, and func kinds, but struct types should also be supported based on the logic in getValue method (lines 157-161) and the error message context.

 	switch outTyp.Kind() {
-	case reflect.Ptr, reflect.Interface, reflect.Func:
+	case reflect.Ptr, reflect.Interface, reflect.Func, reflect.Struct:
 	default:
 		assert.Must(errors.Err{
-			Msg:    "provider type kind error, the supported type kinds are <ptr,interface,func>",
+			Msg:    "provider type kind error, the supported type kinds are <ptr,interface,func,struct>",
 			Detail: fmt.Sprintf("type=%s kind=%s", outTyp, outTyp.Kind()),
 		})
 	}

230-244: Function injection validation is overly strict and may break existing code.

The new validation requires injectable functions to either have no return value or return only an error type. This is a significant breaking change that may not be backward compatible.

Consider making this validation configurable or providing a migration path:

+func (x *Dix) injectFunc(vp reflect.Value, opt Options) {
+	// Allow legacy behavior through options
+	if !opt.StrictFunctionValidation {
+		// Original less strict validation
+		assert.If(vp.Type().NumOut() > 2, "func output num should <=2")
+		assert.If(vp.Type().NumIn() == 0, "func input num should not be zero")
+		// ... rest of original logic
+		return
+	}
+	
+	// Strict validation for new code
 	assert.If(vp.Type().NumOut() > 1, "func output num should <=1")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bd5cf0 and 368531a.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • dixinternal/dix.go (12 hunks)
  • dixinternal/provider.go (1 hunks)
  • example/go.mod (1 hunks)
  • go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/go.mod
  • go.mod
🧰 Additional context used
🪛 GitHub Check: lint
dixinternal/dix.go

[failure] 145-145:
cannot use x.objects[outTyp] (map index expression of type map[group][]value) as "github.com/pubgo/funk/v2/result".Result[map[group][]value] value in return statement

🪛 GitHub Actions: Lint
dixinternal/dix.go

[error] 6-6: golangci-lint: could not import github.com/pubgo/dix/dixinternal

🔇 Additional comments (8)
dixinternal/provider.go (2)

52-64: Excellent error handling and logging implementation.

The reflective function call with comprehensive error logging provides valuable debugging information including function stack traces, input/output types, and values. This will greatly help with troubleshooting dependency injection issues.


66-82: Clean helper function implementation.

The string builder usage and type annotation logic correctly formats provider input types for debugging purposes.

dixinternal/dix.go (6)

87-104: Excellent logging enhancements for debugging.

The addition of timing information, function stack traces, and detailed provider evaluation logging will significantly improve debugging capabilities for complex dependency graphs.


105-112: Robust error handling for provider functions.

The explicit check for error return values from provider functions with proper error wrapping and context information is a significant improvement over the previous implementation.


157-161: Good addition of struct type handling.

Creating new instances and injecting dependencies for struct types fills an important gap in the DI container's capabilities.


314-322: Improved error handling in inject method.

The change from panic-based error handling to returning errors with proper recovery patterns makes the API more robust and user-friendly.


365-377: Enhanced provider function validation.

The explicit validation of error return types in provider functions with clear error messages improves type safety and developer experience.


432-436: Excellent API documentation.

The detailed documentation clearly explains the constraints and behavior of the provide method, which will help developers use the DI container correctly.

Comment on lines 21 to 34
func (v providerInputType) Validate() error {
if v.isMap && !isMapListSupportedType(v.typ) {
return errors.Format("input map value type kind not support, kind=%s", v.typ.Kind().String())
}

if v.isList && !isMapListSupportedType(v.typ) {
return errors.Format("input list element value type kind not support, kind=%s", v.typ.Kind().String())
}

if !isMapListSupportedType(v.typ) {
return errors.Format("input value type kind not support, kind=%s", v.typ.Kind().String())
}

return nil
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify redundant validation logic.

The validation method performs the same type check three times with identical conditions. This can be simplified to a single check.

 func (v providerInputType) Validate() error {
-	if v.isMap && !isMapListSupportedType(v.typ) {
-		return errors.Format("input map value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	if v.isList && !isMapListSupportedType(v.typ) {
-		return errors.Format("input list element value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	if !isMapListSupportedType(v.typ) {
-		return errors.Format("input value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	return nil
+	if !isMapListSupportedType(v.typ) {
+		context := "input value"
+		if v.isMap {
+			context = "input map value"
+		} else if v.isList {
+			context = "input list element value"
+		}
+		return errors.Format("%s type kind not support, kind=%s", context, v.typ.Kind().String())
+	}
+	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (v providerInputType) Validate() error {
if v.isMap && !isMapListSupportedType(v.typ) {
return errors.Format("input map value type kind not support, kind=%s", v.typ.Kind().String())
}
if v.isList && !isMapListSupportedType(v.typ) {
return errors.Format("input list element value type kind not support, kind=%s", v.typ.Kind().String())
}
if !isMapListSupportedType(v.typ) {
return errors.Format("input value type kind not support, kind=%s", v.typ.Kind().String())
}
return nil
func (v providerInputType) Validate() error {
if !isMapListSupportedType(v.typ) {
context := "input value"
if v.isMap {
context = "input map value"
} else if v.isList {
context = "input list element value"
}
return errors.Format(
"%s type kind not support, kind=%s",
context,
v.typ.Kind().String(),
)
}
return nil
}
🤖 Prompt for AI Agents
In dixinternal/provider.go around lines 21 to 34, the Validate method
redundantly checks the same type condition three times with identical logic.
Simplify this by consolidating the repeated isMap, isList, and default checks
into a single type validation check that covers all cases, removing duplicate
calls to isMapListSupportedType and redundant error returns.

- 优化错误处理,使用 result.Result 类型统一处理错误
-重构依赖注入逻辑,提高代码的可读性和可维护性
- 更新 go.mod 和 go.sum 文件,升级 funk 依赖到 v0.5.66-alpha.2版本
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
dixinternal/provider.go (1)

21-35: Simplify redundant validation logic.

The validation method performs the same type check three times with identical conditions. This can be simplified to a single check.

 func (v providerInputType) Validate() error {
-	if v.isMap && !isMapListSupportedType(v.typ) {
-		return errors.Errorf("input map value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	if v.isList && !isMapListSupportedType(v.typ) {
-		return errors.Errorf("input list element value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	if !isMapListSupportedType(v.typ) {
-		return errors.Errorf("input value type kind not support, kind=%s", v.typ.Kind().String())
-	}
-
-	return nil
+	if !isMapListSupportedType(v.typ) {
+		context := "input value"
+		if v.isMap {
+			context = "input map value"
+		} else if v.isList {
+			context = "input list element value"
+		}
+		return errors.Errorf("%s type kind not support, kind=%s", context, v.typ.Kind().String())
+	}
+	return nil
 }
dixinternal/dix.go (2)

56-60: Type kind validation is overly restrictive.

The assertion only allows ptr, interface, and func kinds, but the error message and later code suggest struct should also be supported. This will cause runtime errors for valid struct types.

 	switch outTyp.Kind() {
-	case reflect.Ptr, reflect.Interface, reflect.Func:
+	case reflect.Ptr, reflect.Interface, reflect.Func, reflect.Struct:
 	default:
-		return r.WithErrorf("provider type kind error, the supported type kinds are <ptr,interface,func>, type=%s kind=%s", outTyp, outTyp.Kind())
+		return r.WithErrorf("provider type kind error, the supported type kinds are <ptr,interface,func,struct>, type=%s kind=%s", outTyp, outTyp.Kind())
 	}

232-246: Function injection validation is overly strict and may break existing code.

The new validation requires injectable functions to either have no return value or return only an error type. This is a significant breaking change that may not be backward compatible.

Consider making this validation configurable through options:

 func (x *Dix) injectFunc(vp reflect.Value, opt Options) (r result.Error) {
 	defer result.RecoveryErr(&r)
 
+	// Allow legacy behavior through options
+	if !opt.StrictFunctionValidation {
+		// Original validation for backward compatibility
+		if vp.Type().NumOut() > 2 {
+			return result.ErrorOf("func output num should <=2")
+		}
+		if vp.Type().NumIn() == 0 {
+			return result.ErrorOf("func input num should not be zero")
+		}
+		// Continue with original injection logic...
+	}
+
 	assert.If(vp.Type().NumOut() > 1, "func output num should <=1")
 	assert.If(vp.Type().NumIn() == 0, "func input num should not be zero")

Also add StrictFunctionValidation bool field to the Options struct in option.go.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 368531a and 2ecbe64.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • dixinternal/api.go (1 hunks)
  • dixinternal/dix.go (1 hunks)
  • dixinternal/provider.go (1 hunks)
  • example/go.mod (1 hunks)
  • go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • example/go.mod
  • dixinternal/api.go
  • go.mod
🧰 Additional context used
🧬 Code Graph Analysis (1)
dixinternal/dix.go (4)
dixinternal/option.go (2)
  • Option (4-4)
  • Options (5-8)
dix.go (5)
  • Option (14-14)
  • Dix (16-16)
  • Options (15-15)
  • New (24-26)
  • InjectMethodPrefix (10-10)
dixinternal/api.go (1)
  • New (11-13)
dixinternal/aaa.go (1)
  • InjectMethodPrefix (14-14)
🔇 Additional comments (3)
dixinternal/provider.go (1)

52-64: Well-structured error handling and logging.

The call method provides excellent debugging context with comprehensive error logging including call stack, function type, input values, and types.

dixinternal/dix.go (2)

194-225: Excellent error handling with comprehensive debugging context.

The error handling provides rich context including type information, values, parent types, options, and provider stacks. This will greatly help with debugging dependency resolution issues.


418-434: Verify circular struct reference handling and clarify commented code.

The recursive registration of struct fields could potentially cause issues with circular struct references. Also, line 419 has a commented isStruct assignment that may be needed for proper type tracking.

#!/bin/bash
# Description: Check if there's any circular reference detection for struct types

# Search for cycle detection implementation
ast-grep --pattern 'func $_($$) {
  $$$
  cycle
  $$$
}'

# Check how isStruct field is used elsewhere
rg -A 5 "isStruct" dixinternal/

Comment on lines +482 to +484
assert.If(typ.IsVariadic(), "the func of provider variable parameters are not allowed")
assert.If(typ.NumOut() == 0, "the func of provider output num should not be zero")
assert.If(typ.NumOut() > 2, "the func of provider output num should >= two")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect assertion message.

The assertion message on line 484 contradicts the condition being checked.

 	assert.If(typ.IsVariadic(), "the func of provider variable parameters are not allowed")
 	assert.If(typ.NumOut() == 0, "the func of provider output num should not be zero")
-	assert.If(typ.NumOut() > 2, "the func of provider output num should >= two")
+	assert.If(typ.NumOut() > 2, "the func of provider output num should <= 2")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert.If(typ.IsVariadic(), "the func of provider variable parameters are not allowed")
assert.If(typ.NumOut() == 0, "the func of provider output num should not be zero")
assert.If(typ.NumOut() > 2, "the func of provider output num should >= two")
assert.If(typ.IsVariadic(), "the func of provider variable parameters are not allowed")
assert.If(typ.NumOut() == 0, "the func of provider output num should not be zero")
assert.If(typ.NumOut() > 2, "the func of provider output num should <= 2")
🤖 Prompt for AI Agents
In dixinternal/dix.go around lines 482 to 484, the assertion message on line 484
incorrectly states "the func of provider output num should >= two" while the
condition checks if the number of outputs is greater than 2. Update the
assertion message to correctly reflect the condition, such as "the func of
provider output num should be <= 2" or a message consistent with the condition
typ.NumOut() > 2.

kooksee added 2 commits June 27, 2025 22:18
- Update error handling in dixinternal/dix.go to use errors.WrapErr instead of errors.NewErr
- Upgrade github.com/pubgo/funk dependency to v0.5.66-alpha.3 in go.mod files
- Rename error variable to 'r' for consistency- Use CatchErr() method to handle errors from injectFunc calls
- Refactor error creation using WrapErr() for better error context
- Remove commented-out debug code
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
dixinternal/dix.go (2)

232-246: Function injection validation is overly strict and may break existing code.

The validation requiring injectable functions to have at most one return value (which must be an error type) is a significant breaking change. This has been noted in previous reviews and should be addressed.


486-486: Fix incorrect assertion message.

The assertion message contradicts the condition being checked, as noted in previous reviews.

-	assert.If(typ.NumOut() > 2, "the func of provider output num should >= two")
+	assert.If(typ.NumOut() > 2, "the func of provider output num should be <= 2")
🧹 Nitpick comments (2)
dixinternal/dix.go (2)

39-39: Consider documenting the self-registration pattern.

Registering the container itself as a provider is a clever design but may not be immediately obvious to users. Consider adding a comment explaining this pattern.

+	// Register the container itself as a provider to enable self-injection
 	c.provide(func() *Dix { return c })

421-421: Remove commented code.

The commented line should be removed to keep the code clean.

-		// n.output.isStruct = true
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40a89c0 and 129e2c4.

📒 Files selected for processing (2)
  • dixinternal/dix.go (1 hunks)
  • example/test-return-error/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • example/test-return-error/main.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
dixinternal/dix.go (4)
dixinternal/option.go (2)
  • Option (4-4)
  • Options (5-8)
dix.go (5)
  • Option (14-14)
  • Dix (16-16)
  • Options (15-15)
  • New (24-26)
  • InjectMethodPrefix (10-10)
dixinternal/api.go (1)
  • New (11-13)
dixinternal/aaa.go (1)
  • InjectMethodPrefix (14-14)
🔇 Additional comments (4)
dixinternal/dix.go (4)

56-60: Type validation logic is correct by design.

The validation restricts provider output types to pointer, interface, and function types, while struct types are handled differently via direct instantiation in the getValue method. This design separation is appropriate.


155-159: Struct handling logic is well-designed.

The direct instantiation of struct types with dependency injection is a clean approach that avoids the complexity of provider registration for structs.


289-329: Struct injection logic is well-implemented.

The method correctly handles various field types including nested structs, with proper error handling and type checking.


366-375: Injection method prefix pattern is well-implemented.

The ability to inject dependencies into methods with a specific prefix (DixInject) provides a clean API for method-based dependency injection.

}
}
default:
log.Error().Msgf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using panic or return error for programming errors.

Similar to line 457, using log.Error for incorrect output types may hide programming errors. Consider using panic or returning an error to make these issues more visible.

-		log.Error().Msgf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String())
+		panic(fmt.Sprintf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String()))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log.Error().Msgf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String())
- log.Error().Msgf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String())
+ panic(fmt.Sprintf("incorrect output type, ouTyp=%s kind=%s fnVal=%s", outTyp, outTyp.Kind(), fnVal.String()))
🤖 Prompt for AI Agents
In dixinternal/dix.go at line 438, the current use of log.Error to report
incorrect output types may obscure programming errors. Replace the log.Error
call with a panic that includes the formatted error message or modify the
function to return an error indicating the incorrect output type, ensuring the
issue is immediately visible and handled appropriately.

case reflect.Slice:
input = append(input, &providerInputType{typ: inTye.Elem(), isList: true})
default:
log.Error().Msgf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using panic or return error for programming errors.

Using log.Error for incorrect input types may hide programming errors. Consider using panic with a descriptive message or returning an error to make these issues more visible during development.

-		log.Error().Msgf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind())
+		panic(fmt.Sprintf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind()))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log.Error().Msgf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind())
- log.Error().Msgf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind())
+ panic(fmt.Sprintf("incorrect input type, inTyp=%s kind=%s", inTye, inTye.Kind()))
🤖 Prompt for AI Agents
In dixinternal/dix.go at line 457, the code logs an error for incorrect input
types using log.Error, which may obscure programming errors. Replace this
log.Error call with a panic that includes a descriptive message about the
incorrect input type, or modify the function to return an error instead,
ensuring that such issues are immediately visible and handled during
development.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
dixinternal/dix.go (4)

40-40: Document the self-registration purpose.

Consider adding a comment explaining why the container registers itself as a dependency, as this pattern might not be immediately obvious to other developers.

+	// Self-register the container to allow injection of the DI container itself
 	c.provide(func() *Dix { return c })

45-50: Consider thread safety for concurrent access.

The Dix struct contains multiple maps that could be accessed concurrently. Consider adding synchronization mechanisms (e.g., sync.RWMutex) to prevent race conditions in multi-threaded environments.

 type Dix struct {
+	mu          sync.RWMutex
 	option      Options
 	providers   map[outputType][]*providerFn
 	objects     map[outputType]map[group][]value
 	initializer map[reflect.Value]bool
 }

424-426: Remove or implement the commented struct handling.

The commented line suggests incomplete implementation for struct output type handling. Either implement the intended functionality or remove the comment to avoid confusion.

 	case reflect.Struct:
-		// n.output.isStruct = true
+		n.output = &providerOutputType{typ: outTyp, isStruct: true}

498-499: Address TODO: Add support for second error parameter.

The TODO indicates that error handling support for the second return value needs to be implemented. This functionality appears to be partially implemented in handleProvide but needs completion.

Would you like me to create an issue to track this enhancement for adding full error support in provider functions?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 129e2c4 and 87a0dfb.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • dixinternal/dix.go (1 hunks)
  • example/go.mod (1 hunks)
  • example/test-return-error/main.go (1 hunks)
  • go.mod (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • example/test-return-error/main.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/go.mod
  • go.mod

@kooksee kooksee merged commit 00ef2f5 into master Jun 28, 2025
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant