1 module des.log.rule;
2 
3 import std.algorithm : min;
4 import std.string : split, join;
5 
6 import des.log.base;
7 
8 /// store rules for logging
9 synchronized class Rule
10 {
11 package:
12     shared Rule parent; ///
13 
14     LogLevel level = LogLevel.ERROR; ///
15     shared Rule[string] inner; ///
16 
17     bool use_minimal = true; ///
18 
19 public:
20 
21     ///
22     this( shared Rule parent = null )
23     {
24         this.parent = parent;
25         if( parent )
26             this.level = parent.level;
27     }
28 
29     @property
30     {
31         ///
32         bool useMinimal() const
33         {
34             if( parent !is null )
35                 return parent.useMinimal();
36             else return use_minimal;
37         }
38 
39         ///
40         void useMinimal( bool um )
41         {
42             if( parent !is null )
43                 parent.useMinimal = um;
44             else use_minimal = um;
45         }
46     }
47 
48     /// setting allowed level for emitter (create new inner Rule), if emitter is "" sets self level
49     void setLevel( LogLevel lvl, string emitter="" )
50     {
51         auto addr = splitAddress( emitter );
52         if( addr[0].length == 0 ) { level = lvl; return; }
53         auto iname = addr[0];
54         if( iname !in inner ) inner[iname] = new shared Rule(this);
55         inner[iname].setLevel( lvl, addr[1] );
56     }
57 
58     /// if emitter is "" returns self level
59     LogLevel allowedLevel( string emitter="" )
60     {
61         auto addr = splitAddress( emitter );
62         if( addr[0].length == 0 ) return level;
63         auto iname = addr[0];
64         if( iname !in inner ) return level;
65         if( useMinimal )
66             return min( level, inner[iname].allowedLevel( addr[1] ) );
67         else
68             return inner[iname].allowedLevel( addr[1] );
69     }
70 
71     string print( string offset="" ) const
72     {
73         string ret = format( "%s", level );
74         foreach( key, val; inner )
75             ret ~= format( "\n%s%s : %s", offset, key, val.print( offset ~ mlt(" ",key.length) ) );
76         return ret;
77     }
78 
79 protected:
80 
81     ///
82     static string[2] splitAddress( string emitter )
83     {
84         auto addr = emitter.split(".");
85         if( addr.length == 0 ) return ["",""];
86         if( addr.length == 1 ) return [addr[0],""];
87 
88         return [ addr[0], addr[1..$].join(".") ];
89     }
90 }
91 
92 private T[] mlt(T)( T[] val, size_t cnt ) nothrow
93 {
94     T[] buf;
95     foreach( i; 0 .. cnt ) buf ~= val;
96     return buf;
97 }
98 
99 unittest { assert( "    ", mlt( " ", 4 ) ); }
100 
101 ///
102 unittest
103 {
104     auto r = new shared Rule;
105 
106     r.setLevel( LogLevel.INFO );
107     r.setLevel( LogLevel.TRACE, "des.gl" );
108     r.setLevel( LogLevel.WARN, "des" );
109 
110     assert( r.allowedLevel() == LogLevel.INFO );
111     assert( r.allowedLevel("des") == LogLevel.WARN );
112     assert( r.allowedLevel("des.gl") == LogLevel.WARN );
113 
114     r.use_minimal = false;
115 
116     assert( r.allowedLevel() == LogLevel.INFO );
117     assert( r.allowedLevel("des") == LogLevel.WARN );
118     assert( r.allowedLevel("des.gl") == LogLevel.TRACE );
119 }