1+ package main
2+
3+ import (
4+ "context"
5+ "fmt"
6+ "log"
7+ "strings"
8+
9+ "go.uber.org/fx"
10+ )
11+
12+ // =====================================================
13+ // DEMO: Map Value Groups + Decoration in Fx
14+ // =====================================================
15+
16+ // Logger interface for different log outputs
17+ type Logger interface {
18+ Log (message string )
19+ Name () string
20+ }
21+
22+ // FileLogger logs to files
23+ type FileLogger struct { filename string }
24+
25+ func (l * FileLogger ) Log (message string ) {
26+ fmt .Printf ("📁 FileLogger[%s]: %s\n " , l .filename , message )
27+ }
28+
29+ func (l * FileLogger ) Name () string {
30+ return "file"
31+ }
32+
33+ // ConsoleLogger logs to console
34+ type ConsoleLogger struct {}
35+
36+ func (l * ConsoleLogger ) Log (message string ) {
37+ fmt .Printf ("🖥️ ConsoleLogger: %s\n " , message )
38+ }
39+
40+ func (l * ConsoleLogger ) Name () string {
41+ return "console"
42+ }
43+
44+ // DatabaseLogger logs to database
45+ type DatabaseLogger struct {}
46+
47+ func (l * DatabaseLogger ) Log (message string ) {
48+ fmt .Printf ("🗄️ DatabaseLogger: %s\n " , message )
49+ }
50+
51+ func (l * DatabaseLogger ) Name () string {
52+ return "database"
53+ }
54+
55+ // =====================================================
56+ // PROVIDERS
57+ // =====================================================
58+
59+ func ProvideFileLogger () Logger {
60+ return & FileLogger {filename : "app.log" }
61+ }
62+
63+ func ProvideConsoleLogger () Logger {
64+ return & ConsoleLogger {}
65+ }
66+
67+ func ProvideDatabaseLogger () Logger {
68+ return & DatabaseLogger {}
69+ }
70+
71+ // =====================================================
72+ // MAP DECORATION EXAMPLE
73+ // =====================================================
74+
75+ type LoggerDecorationParams struct {
76+ fx.In
77+ // Input: map of loggers by name
78+ Loggers map [string ]Logger `group:"loggers"`
79+ }
80+
81+ type LoggerDecorationResult struct {
82+ fx.Out
83+ // Output: enhanced map of loggers
84+ Loggers map [string ]Logger `group:"loggers"`
85+ }
86+
87+ // DecorateLoggers demonstrates map decoration - add prefix to all loggers
88+ func DecorateLoggers (params LoggerDecorationParams ) LoggerDecorationResult {
89+ fmt .Println ("\n 🎨 Decorating loggers with [ENHANCED] prefix..." )
90+
91+ enhancedLoggers := make (map [string ]Logger )
92+
93+ for name , logger := range params .Loggers {
94+ // Wrap each logger with enhancement
95+ enhancedLoggers [name ] = & EnhancedLogger {
96+ wrapped : logger ,
97+ prefix : "[ENHANCED]" ,
98+ }
99+ fmt .Printf (" ✨ Enhanced logger: %s\n " , name )
100+ }
101+
102+ return LoggerDecorationResult {Loggers : enhancedLoggers }
103+ }
104+
105+ // EnhancedLogger wraps another logger with a prefix
106+ type EnhancedLogger struct {
107+ wrapped Logger
108+ prefix string
109+ }
110+
111+ func (l * EnhancedLogger ) Log (message string ) {
112+ l .wrapped .Log (fmt .Sprintf ("%s %s" , l .prefix , message ))
113+ }
114+
115+ func (l * EnhancedLogger ) Name () string {
116+ return l .wrapped .Name ()
117+ }
118+
119+ // =====================================================
120+ // LOGGING SERVICE USING DECORATED MAP
121+ // =====================================================
122+
123+ type LoggingServiceParams struct {
124+ fx.In
125+
126+ // 🎯 This map will contain DECORATED loggers!
127+ LoggerMap map [string ]Logger `group:"loggers"`
128+ }
129+
130+ type LoggingService struct {
131+ loggers map [string ]Logger
132+ }
133+
134+ func NewLoggingService (params LoggingServiceParams ) * LoggingService {
135+ fmt .Printf ("\n 🔧 LoggingService created with %d decorated loggers\n " , len (params .LoggerMap ))
136+
137+ // Show what we received
138+ for name := range params .LoggerMap {
139+ fmt .Printf (" 📄 Logger available: %s\n " , name )
140+ }
141+
142+ return & LoggingService {loggers : params .LoggerMap }
143+ }
144+
145+ func (s * LoggingService ) LogToSpecific (loggerName , message string ) error {
146+ logger , exists := s .loggers [loggerName ]
147+ if ! exists {
148+ return fmt .Errorf ("logger %q not found" , loggerName )
149+ }
150+
151+ logger .Log (message )
152+ return nil
153+ }
154+
155+ func (s * LoggingService ) LogToAll (message string ) {
156+ fmt .Println ("\n 📢 Broadcasting to all loggers:" )
157+ for _ , logger := range s .loggers {
158+ logger .Log (message )
159+ }
160+ }
161+
162+ func (s * LoggingService ) LogToFiltered (message string , filter func (name string ) bool ) {
163+ fmt .Printf ("\n 🔍 Logging to filtered loggers (%s):\n " , "contains 'log'" )
164+ for name , logger := range s .loggers {
165+ if filter (name ) {
166+ logger .Log (message )
167+ }
168+ }
169+ }
170+
171+ // =====================================================
172+ // APPLICATION LIFECYCLE
173+ // =====================================================
174+
175+ func RunDecorationDemo (lc fx.Lifecycle , service * LoggingService ) {
176+ lc .Append (fx.Hook {
177+ OnStart : func (ctx context.Context ) error {
178+ fmt .Println ("\n 🚀 Starting Map Decoration Demo" )
179+ fmt .Println ("===================================" )
180+
181+ // Test specific logger
182+ fmt .Println ("\n 🎯 Testing specific logger (decorated):" )
183+ service .LogToSpecific ("console" , "This is a console message" )
184+
185+ // Test all loggers (decorated)
186+ service .LogToAll ("This message goes to all decorated loggers" )
187+
188+ // Test filtered logging (using map keys)
189+ service .LogToFiltered ("Filtered message" , func (name string ) bool {
190+ return strings .Contains (name , "log" ) // This won't match our current names
191+ })
192+
193+ // Test another filter
194+ fmt .Printf ("\n 🔍 Logging to filtered loggers (%s):\n " , "starts with 'c'" )
195+ service .LogToFiltered ("Another filtered message" , func (name string ) bool {
196+ return strings .HasPrefix (name , "c" )
197+ })
198+
199+ fmt .Println ("\n ✅ Map decoration demo completed successfully!" )
200+ fmt .Println ("\n 💡 Key insights:" )
201+ fmt .Println (" - Map value groups can be decorated just like slices" )
202+ fmt .Println (" - Decorators receive map[string]T and return map[string]T" )
203+ fmt .Println (" - All loggers were enhanced with [ENHANCED] prefix" )
204+ fmt .Println (" - Map keys (names) are preserved through decoration" )
205+ fmt .Println (" - Easy filtering and lookup by name" )
206+
207+ return nil
208+ },
209+ })
210+ }
211+
212+ // =====================================================
213+ // MAIN APPLICATION
214+ // =====================================================
215+
216+ func main () {
217+ app := fx .New (
218+ // Provide loggers with names and groups
219+ fx .Provide (
220+ fx .Annotate (
221+ ProvideFileLogger ,
222+ fx .ResultTags (`name:"file" group:"loggers"` ),
223+ ),
224+ fx .Annotate (
225+ ProvideConsoleLogger ,
226+ fx .ResultTags (`name:"console" group:"loggers"` ),
227+ ),
228+ fx .Annotate (
229+ ProvideDatabaseLogger ,
230+ fx .ResultTags (`name:"database" group:"loggers"` ),
231+ ),
232+ ),
233+
234+ // 🎨 DECORATE the logger map - add enhancements
235+ fx .Decorate (DecorateLoggers ),
236+
237+ // Provide the logging service (receives decorated loggers)
238+ fx .Provide (NewLoggingService ),
239+
240+ // Register the demo lifecycle hook
241+ fx .Invoke (RunDecorationDemo ),
242+
243+ // Suppress logs for cleaner demo output
244+ fx .NopLogger ,
245+ )
246+
247+ fmt .Println ("🎨 Fx Map Value Groups + Decoration Demo" )
248+ fmt .Println ("==========================================" )
249+ fmt .Println ("This demo shows map decoration with our new feature!" )
250+
251+ if err := app .Start (context .Background ()); err != nil {
252+ log .Fatal (err )
253+ }
254+
255+ if err := app .Stop (context .Background ()); err != nil {
256+ log .Fatal (err )
257+ }
258+ }
0 commit comments