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     bool only_register = false;
19     bool registred = false;
20 
21 public:
22 
23     ///
24     this( shared Rule parent = null )
25     {
26         this.parent = parent;
27         if( parent ) this.level = parent.level;
28     }
29 
30     @property
31     {
32         ///
33         bool useMinimal() const
34         {
35             if( parent !is null ) return parent.useMinimal;
36             else return use_minimal;
37         }
38 
39         ///
40         bool useMinimal( bool v )
41         {
42             if( parent !is null ) return parent.useMinimal = v;
43             else return use_minimal = v;
44         }
45 
46         ///
47         bool onlyRegister() const
48         {
49             if( parent !is null ) return parent.onlyRegister;
50             else return only_register;
51         }
52 
53         ///
54         bool onlyRegister( bool v )
55         {
56             if( parent !is null ) return parent.onlyRegister = v;
57             else return only_register = v;
58         }
59     }
60 
61     /// setting allowed level for emitter (create new inner Rule), if emitter is "" sets self level
62     void setLevel( LogLevel lvl, string emitter="" )
63     { setLevelImpl( lvl, emitter.split(".") ); }
64 
65     /// if emitter is "" returns self level
66     LogLevel allowedLevel( string emitter="" ) const
67     { return allowedLevelImpl( emitter.split(".") ); }
68 
69     /// test is message allowed for this rule
70     bool isAllowed( in LogMessage lm ) const
71     { return allowedLevel( lm.emitter ) >= lm.level; }
72 
73     /// return string what represent of rule structure
74     string strRepresent() const { return strRepresentImpl(); }
75 
76 protected:
77 
78     void setLevelImpl( LogLevel lvl, string[] emitter )
79     {
80         if( emitter.length == 0 || emitter[0].length == 0 )
81         {
82             level = lvl;
83             registred = true;
84             return;
85         }
86 
87         auto iname = emitter[0];
88 
89         if( iname !in inner )
90             inner[iname] = new shared Rule(this);
91 
92         inner[iname].setLevelImpl( lvl, emitter.length > 1 ? emitter[1..$] : [] );
93     }
94 
95     /// if emitter is "" returns self level
96     LogLevel allowedLevelImpl( string[] emitter ) const
97     {
98         if( emitter.length == 0 || emitter[0].length == 0 )
99             return level;
100 
101         auto iname = emitter[0];
102 
103         if( iname !in inner )
104         {
105             if( onlyRegister )
106             {
107                 if( !(registred && parent !is null) )
108                     return LogLevel.OFF;
109                 else
110                     return level;
111             }
112             else return level;
113         }
114 
115         auto childnames = emitter.length > 1 ? emitter[1..$] : [];
116 
117         if( useMinimal )
118             return min( level, inner[iname].allowedLevelImpl( childnames ) );
119         else
120             return inner[iname].allowedLevelImpl( childnames );
121     }
122 
123     string strRepresentImpl( string offset="", bool first=true ) const
124     {
125         string ret = format( "%s%s", level, registred ? " [reg]" : "" );
126         if( first )
127             ret ~= format( "%s%s", useMinimal ? " use minimal" : "",
128                                  onlyRegister ? " only register" : "" );
129         foreach( key, val; inner )
130             ret ~= format( "\n%s%s : %s", offset, key,
131                     val.strRepresentImpl( offset ~ mlt(" ",key.length), false ) );
132         return ret;
133     }
134 }
135 
136 private T[] mlt(T)( T[] val, size_t cnt ) nothrow
137 {
138     T[] buf;
139     foreach( i; 0 .. cnt ) buf ~= val;
140     return buf;
141 }
142 
143 unittest { assert( "    ", mlt( " ", 4 ) ); }
144 
145 ///
146 unittest
147 {
148     auto r = new shared Rule;
149 
150     r.setLevel( LogLevel.INFO );
151     r.setLevel( LogLevel.TRACE, "des.gl" );
152     r.setLevel( LogLevel.WARN, "des" );
153 
154     assert( r.allowedLevel() == LogLevel.INFO );
155     assert( r.allowedLevel("des") == LogLevel.WARN );
156     assert( r.allowedLevel("des.gl") == LogLevel.WARN );
157 
158     r.use_minimal = false;
159 
160     assert( r.allowedLevel() == LogLevel.INFO );
161     assert( r.allowedLevel("des") == LogLevel.WARN );
162     assert( r.allowedLevel("des.gl") == LogLevel.TRACE );
163 }