Skip to content

Commit 2b23a85

Browse files
add bif debug
1 parent 2a9ad41 commit 2b23a85

File tree

9 files changed

+491
-1
lines changed

9 files changed

+491
-1
lines changed

doc/bif-debug.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
{:debug; ... :}
2+
===============
3+
4+
Outputs the schema key if debug is enabled.
5+
6+
```html
7+
{:debug; data->key :}
8+
```
9+
10+
For security, debug should be disabled in production. Even so, remove "debug" bifs or wrap them to prevent execution in production:
11+
12+
```html
13+
{:bool; user_rol_developper >>
14+
{:debug; full-schema :}
15+
:}
16+
```
17+
18+
In the previous example, your application will be responsible for managing "user_rol_developper" or any other variable used.
19+
20+
Stray debug bifs show an error message with a reminder to be removed in production.
21+
22+
Modifiers:
23+
----------
24+
25+
```html
26+
{:^debug; ... :}
27+
{:!debug; ... :}
28+
```
29+
### Modifier: ^ (upline)
30+
31+
Eliminates previous whitespaces, (See "unprintable" for examples.)
32+
33+
### Modifier: ! (not):
34+
35+
Does not display output, except for "unprintable" if debug is active, nor does it show any error.
36+
37+
```html
38+
{:!debug; this is ignored :}
39+
```
40+
It is useful to determine if debug is enabled:
41+
42+
```html
43+
{:!debug; :}{:else; debug is disabled :}{:else; debug is enabled :}
44+
```
45+
46+
The following does not display output or any error:
47+
48+
```html
49+
{:!debug; :}
50+
```
51+
52+
Although any key is ignored, it might be confusing to do this:
53+
54+
```html
55+
{:!debug; data->key :}
56+
```
57+
58+
No flags
59+
--------
60+
61+
Enabling debug
62+
------------
63+
64+
For debug to be active, a file must exist and its modification date must not be expired.
65+
66+
The file name and expiration are set in the "schema" configuration:
67+
68+
```text
69+
{
70+
"config": {
71+
"debug_expire": 36000,
72+
"debug_file": ""
73+
}
74+
}
75+
```
76+
77+
If "debug_file" is empty or the file does not exist, debug is disabled.
78+
79+
If the file's modification date exceeds "debug_expire" in seconds, debug is disabled.
80+
81+
The following example enables debug for 1 hour:
82+
83+
```text
84+
{
85+
"config": {
86+
"debug_expire": 3600,
87+
"debug_file": "/tmp/enable-neutral-debug-h5hdf7sj34xp"
88+
}
89+
}
90+
```
91+
92+
Then:
93+
94+
```text
95+
touch /tmp/enable-neutral-debug-h5hdf7sj34xp
96+
```
97+
98+
If we want to extend the debug duration, we do again:
99+
100+
```text
101+
touch /tmp/enable-neutral-debug-h5hdf7sj34xp
102+
```
103+
104+
Any file name can be used, but it is a good idea to include a random part.
105+
106+
For production, the recommended configuration is:
107+
108+
```text
109+
{
110+
"config": {
111+
"debug_expire": 0,
112+
"debug_file": ""
113+
}
114+
}
115+
```
116+
117+
Examples
118+
--------
119+
120+
There are global data and keys that are available to the entire template, and local data and keys that are available in each block. "Local" information must be preceded by "local::". Some keys are always "local" such as "snippets", "locale", and others.
121+
122+
Displays the full schema, including internal Neutral keys:
123+
124+
```html
125+
{:debug; full-schema :}
126+
```
127+
128+
Displays errors at this point:
129+
130+
```html
131+
{:debug; __error :}
132+
```
133+
134+
Displays the configuration:
135+
136+
```html
137+
{:debug; config :}
138+
```
139+
140+
Displays the context:
141+
142+
```html
143+
{:debug; data->CONTEXT :}
144+
```
145+
146+
Displays all global data:
147+
148+
```html
149+
{:debug; data :}
150+
```
151+
152+
Displays the variable varname:
153+
154+
```html
155+
{:debug; data->varname :}
156+
```
157+
158+
Displays all local data:
159+
160+
```html
161+
{:debug; local::data :}
162+
```
163+
164+
Displays the local variable varname:
165+
166+
```html
167+
{:debug; local::data->varname :}
168+
```
169+
170+
Displays the "snippets" active at this point:
171+
172+
```html
173+
{:debug; local::snippets :}
174+
```
175+
176+
Displays "locale":
177+
178+
```html
179+
{:debug; local::locale :}
180+
```
181+
182+
Displays the active translations at this point:
183+
184+
```html
185+
{:debug; local::locale->trans :}
186+
```
187+
188+
Displays the current language:
189+
190+
```html
191+
{:debug; local::locale->current :}
192+
```
193+
194+
Displays the "declare" active at this point:
195+
196+
```html
197+
{:debug; local::declare :}
198+
```
199+
200+
Displays the "params" active at this point:
201+
202+
```html
203+
{:debug; local::params :}
204+
```
205+
206+
If cache is active, the output will also be saved to the cache. If you wish to avoid this behavior, you can do so like this:
207+
208+
```html
209+
{:!cache;
210+
{:debug; data :}
211+
:}
212+
```
213+
214+
---

src/bif/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod parse_bif_trans;
4848
mod parse_bif_unknown;
4949
mod parse_bif_var;
5050
mod parse_bif_obj;
51+
mod parse_bif_debug;
5152

5253
mod exec_python;
5354
pub use exec_python::PythonExecutor;
@@ -200,6 +201,7 @@ impl<'a> Bif<'a> {
200201
"sum" => result = self.parse_bif_sum(),
201202
"trans" => result = self.parse_bif_trans(),
202203
"obj" => result = self.parse_bif_obj(),
204+
"debug" => result = self.parse_bif_debug(),
203205
_ => result = self.parse_bif_unknown(),
204206
}
205207

src/bif/parse_bif_debug.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#![doc = include_str!("../../doc/bif-debug.md")]
2+
3+
use crate::{bif::constants::*, bif::Bif, bif::BifError, constants::*};
4+
use std::fs;
5+
use std::path::Path;
6+
use std::time::{SystemTime, Duration};
7+
8+
impl<'a> Bif<'a> {
9+
/*
10+
{:debug; data->key :}
11+
*/
12+
pub(crate) fn parse_bif_debug(&mut self) -> Result<(), BifError> {
13+
if self.mod_filter || self.mod_scope {
14+
return Err(self.bif_error(BIF_ERROR_MODIFIER_NOT_ALLOWED));
15+
}
16+
17+
let debug_enable = self.debug_enable();
18+
19+
if self.mod_negate {
20+
if debug_enable {
21+
self.out = UNPRINTABLE.to_string();
22+
} else {
23+
self.out = EMPTY_STRING;
24+
}
25+
26+
return Ok(());
27+
}
28+
29+
if !debug_enable {
30+
self.out = EMPTY_STRING;
31+
if self.mod_negate {
32+
return Ok(());
33+
} else {
34+
return Err(self.bif_error("Debug is disabled. Remember to remove the bif debug in production."));
35+
}
36+
}
37+
38+
self.extract_params_code(true);
39+
40+
let mut schema = &self.shared.schema;
41+
let mut key_name = self.code.clone();
42+
if key_name.starts_with("local::") {
43+
key_name = key_name.strip_prefix("local::").unwrap_or("").to_string();
44+
schema = &self.shared.schema["__indir"][&self.inherit.indir];
45+
}
46+
47+
let k = if self.code.is_empty() {
48+
self.out = VERSION.to_string();
49+
return Ok(());
50+
} else if key_name == "full-schema" {
51+
"".to_string()
52+
} else {
53+
format!("/{}", key_name).replace(BIF_ARRAY, "/")
54+
};
55+
56+
self.out = match schema.pointer(&k) {
57+
Some(value) => match serde_json::to_string_pretty(value) {
58+
Ok(json_str) => json_str,
59+
Err(e) => format!("Error formatting JSON: {}", e),
60+
},
61+
None => format!("Undefined: '{}'", self.code),
62+
};
63+
64+
Ok(())
65+
}
66+
67+
/// check if debug is enabled
68+
pub(crate) fn debug_enable(&self) -> bool {
69+
if self.shared.debug_file.is_empty() {
70+
return false;
71+
}
72+
73+
let path = Path::new(&self.shared.debug_file);
74+
75+
if !path.exists() || !path.is_file() {
76+
return false;
77+
}
78+
79+
let metadata = match fs::metadata(path) {
80+
Ok(md) => md,
81+
Err(_) => return false,
82+
};
83+
84+
let modified_time = match metadata.modified() {
85+
Ok(time) => time,
86+
Err(_) => return false,
87+
};
88+
89+
let now = SystemTime::now();
90+
91+
match now.duration_since(modified_time) {
92+
Ok(duration) => duration < Duration::from_secs(self.shared.debug_expire),
93+
Err(_) => false,
94+
}
95+
}
96+
}
97+
98+
99+
#[cfg(test)]
100+
#[path = "parse_bif_debug_tests.rs"]
101+
mod tests;

0 commit comments

Comments
 (0)