Skip to content

Commit 16635bf

Browse files
authored
Merge pull request #13 from TickPoints/dev
Update to `0.1.8-alpha`
2 parents 6163b55 + 9442d8e commit 16635bf

8 files changed

Lines changed: 538 additions & 127 deletions

File tree

Cargo.lock

Lines changed: 66 additions & 92 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mdbook-plotly"
3-
version = "0.1.7"
3+
version = "0.1.8-alpha"
44
authors = ["TickPoints <tickpoints@outlook.com>"]
55
edition = "2024"
66
keywords = ["markdown", "mdbook"]
@@ -40,5 +40,3 @@ lto = "fat"
4040
codegen-units = 1
4141
panic = "abort"
4242
strip = true
43-
# Only on alpha or beta.
44-
debug = true

docs/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## v0.1.8-alpha
4+
- Adjusted `code_handler`
5+
- Updated `map`
6+
- Fixed docs
7+
- Updated tests
8+
39
## v0.1.7
410
- Adjusted `code_handler`
511
- Updated `map`

docs/USAGE-zh_CN.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ offline_js_sources = false
188188
189189
- **Rgb**`"rgb(u8, u8, u8)"`(例如 `"rgb(0, 0, 0)"`
190190
- **Rgba**`"rgba(u8, u8, u8, f64)"`(例如 `"rgba(0, 0, 0, 0.0)"`
191-
- **Color** – 可以是命名的 CSS 颜色(`{ named_color: "aliceblue" }`)、RGB 字符串或 RGBA 字符串。
191+
- **Color** – 可以是:
192+
* 命名的 CSS 颜色:`{ named_color: "aliceblue" }`
193+
* RGB 字符串:`"rgb(255, 0, 0)"`
194+
* RGBA 字符串:`"rgba(255, 0, 0, 0.5)"`
195+
* 或等效的对象形式:`{ rgb_color: "rgb(255,0,0)" }``{ rgba_color: "rgba(255,0,0,0.5)" }`
192196
193197
### 映射与生成器
194198
@@ -300,6 +304,43 @@ offline_js_sources = false
300304
// 得到 [0.0, 0.25, 0.5, 0.75, 1.0]
301305
```
302306
307+
- **`if`** — 根据算术表达式的结果在两个值之间进行条件选择。
308+
309+
_参数:_
310+
```json5
311+
{
312+
type: "if",
313+
condition: String, // 计算结果为 bool 的算术表达式
314+
true: T, // 当 condition = true 时使用的值
315+
false: T // 当 condition = false 时使用的值
316+
}
317+
```
318+
条件使用 [fasteval](https://crates.io/crates/fasteval) 库求值;表达式中没有可用变量。
319+
320+
_示例:_
321+
```json5
322+
{ type: "if", condition: "2 > 1", true: [1,2,3], false: [4,5,6] }
323+
// 得到 [1,2,3],因为 2 > 1 求值为 true
324+
```
325+
326+
- **`time`** — 生成两个时间字符串(开始和结束)的列表。当前直接返回提供的开始和结束字符串;interval 和 format 会被解析但尚未用于生成。
327+
328+
_参数:_
329+
```json5
330+
{
331+
type: "time",
332+
start: String, // 开始时间字符串
333+
end: String, // 结束时间字符串
334+
interval: String, // 时间间隔(当前未使用)
335+
format?: String // 可选的时间格式(当前未使用)
336+
}
337+
```
338+
_示例:_
339+
```json5
340+
{ type: "time", start: "2026-01-01", end: "2026-01-02", interval: "1d" }
341+
// 得到 ["2026-01-01", "2026-01-02"]
342+
```
343+
303344
#### 使用映射
304345
305346
映射条目在其他地方通过前缀 `map.` 引用。例如,如果映射包含键 `myrange`,您可以在任何接受 `DataPack<T>` 的字段(大多数数组和数字字段)中使用 `"map.myrange"`
@@ -452,6 +493,7 @@ config: {
452493
// 确定 plotly 标志是否显示在模式栏的末尾
453494
display_logo?: bool,
454495
// 使用公司标志对图像进行水印处理
496+
watermark?: bool,
455497
}
456498
```
457499

docs/USAGE.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ The following notation is used throughout this reference to describe expected ty
188188
189189
- **Rgb**`"rgb(u8, u8, u8)"` (e.g., `"rgb(0, 0, 0)"`)
190190
- **Rgba**`"rgba(u8, u8, u8, f64)"` (e.g., `"rgba(0, 0, 0, 0.0)"`)
191-
- **Color** – either a named CSS color (`{ named_color: "aliceblue" }`), an RGB string, or an RGBA string.
191+
- **Color** – can be one of the following:
192+
- A named CSS color: `{ named_color: "aliceblue" }`
193+
- An RGB color: `{ rgb_color: "rgb(255, 0, 0)" }`
194+
- An RGBA color: `{ rgba_color: "rgba(255, 0, 0, 0.5)" }`
195+
- Alternatively, for RGB and RGBA, you can use the plain string forms `"rgb(255,0,0)"` or `"rgba(255,0,0,0.5)"` directly.
192196
193197
### Map and Generators
194198
@@ -300,6 +304,43 @@ All generator objects must have a `type` field. The following generator types ar
300304
// yields [0.0, 0.25, 0.5, 0.75, 1.0]
301305
```
302306
307+
- **`if`** — Conditionally selects between two values based on an arithmetic expression.
308+
309+
_Parameters:_
310+
```json5
311+
{
312+
type: "if",
313+
condition: String, // arithmetic expression that evaluates to a number
314+
true: T, // value used when condition ≠ 0.0
315+
false: T // value used when condition = 0.0
316+
}
317+
```
318+
The condition is evaluated using the [fasteval](https://crates.io/crates/fasteval) library; no variables are available.
319+
320+
_Example:_
321+
```json5
322+
{ type: "if", condition: "2 > 1", true: [1,2,3], false: [4,5,6] }
323+
// yields [1,2,3] because 2 > 1 evaluates to 1.0 (non‑zero)
324+
```
325+
326+
- **`time`** — Generates a list of two time strings (start and end). Currently returns the provided start and end strings directly; interval and format are parsed but not yet used for generation.
327+
328+
_Parameters:_
329+
```json5
330+
{
331+
type: "time",
332+
start: String, // start time string
333+
end: String, // end time string
334+
interval: String, // time interval (currently unused)
335+
format?: String // optional time format (currently unused)
336+
}
337+
```
338+
_Example:_
339+
```json5
340+
{ type: "time", start: "2026-01-01", end: "2026-01-02", interval: "1d" }
341+
// yields ["2026-01-01", "2026-01-02"]
342+
```
343+
303344
#### Using the Map
304345
305346
Map entries are referenced elsewhere by prefixing the key with `map.`. For example, if the map contains a key `myrange`, you can refer to it as `"map.myrange"` in any field that accepts a `DataPack<T>` (most array and numeric fields).

src/macros.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ macro_rules! translate {
3232
let target = $target;
3333
$(
3434
let target = if let Some(v) = $value.get_mut(stringify!($method)) {
35-
let data = serde_json::from_value::<DataPack<$ty>>(v.take())?;
36-
target.$method(data.unwrap($map)?)
35+
let data = serde_json::from_value::<DataPack<$ty>>(v.take())
36+
.map_err(|e| ::anyhow::anyhow!("Failed to deserialize field '{}': {}", stringify!($method), e))?;
37+
target.$method(data.unwrap($map)
38+
.map_err(|e| ::anyhow::anyhow!("Failed to unwrap DataPack for field '{}': {}", stringify!($method), e))?)
3739
} else {
3840
target
3941
};
4042
)*
41-
Ok::<_, serde_json::Error>(target)
43+
Ok::<_, ::anyhow::Error>(target)
4244
}};
4345
}
4446

@@ -53,8 +55,10 @@ macro_rules! translate_enum {
5355
let target = $target;
5456
$(
5557
let target = if let Some(v) = $value.get_mut(stringify!($method)) {
56-
let data = serde_json::from_value::<DataPack<String>>(v.take())?;
57-
let s = data.unwrap($map)?;
58+
let data = serde_json::from_value::<DataPack<String>>(v.take())
59+
.map_err(|e| ::anyhow::anyhow!("Failed to deserialize field '{}': {}", stringify!($method), e))?;
60+
let s = data.unwrap($map)
61+
.map_err(|e| ::anyhow::anyhow!("Failed to unwrap DataPack for field '{}': {}", stringify!($method), e))?;
5862
match s.as_str() {
5963
$($str_val => target.$method($variant),)*
6064
unexpected => {

src/preprocessor/handlers/code_handler/until.rs

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::{Result, anyhow, Context};
1+
use anyhow::{Context, Result, anyhow};
22
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
33
use serde_json::{Map as JsonMap, Value, value::Index};
44
use std::fmt::{Debug, Display};
@@ -60,14 +60,22 @@ where
6060
let data = ez_eval(&expr, &mut namespace)?;
6161
result.push(Value::from(data));
6262
}
63-
serde_json::from_value(result.into())
64-
.with_context(|| format!("failed to deserialize generated list for type '{}'", value_type))
63+
serde_json::from_value(result.into()).with_context(|| {
64+
format!(
65+
"failed to deserialize generated list for type '{}'",
66+
value_type
67+
)
68+
})
6569
}
6670
"g-number" => {
6771
let expr: String = must_translate(&mut value, map, "expr")?;
6872
let data = ez_eval(&expr, &mut fasteval::EmptyNamespace {})?;
69-
serde_json::from_value(data.into())
70-
.with_context(|| format!("failed to deserialize generated number for type '{}'", value_type))
73+
serde_json::from_value(data.into()).with_context(|| {
74+
format!(
75+
"failed to deserialize generated number for type '{}'",
76+
value_type
77+
)
78+
})
7179
}
7280
"g-range" => {
7381
let begin: f64 = must_translate(&mut value, map, "begin")?;
@@ -86,15 +94,23 @@ where
8694
result.push(Value::from(current));
8795
current += step;
8896
}
89-
serde_json::from_value(result.into())
90-
.with_context(|| format!("failed to deserialize generated range for type '{}'", value_type))
97+
serde_json::from_value(result.into()).with_context(|| {
98+
format!(
99+
"failed to deserialize generated range for type '{}'",
100+
value_type
101+
)
102+
})
91103
}
92104
"g-repeat" => {
93105
let val: Value = must_translate(&mut value, map, "value")?;
94106
let count: u64 = must_translate(&mut value, map, "count")?;
95107
let result = std::iter::repeat_n(val, count as usize).collect::<Vec<_>>();
96-
serde_json::from_value(result.into())
97-
.with_context(|| format!("failed to deserialize repeated values for type '{}'", value_type))
108+
serde_json::from_value(result.into()).with_context(|| {
109+
format!(
110+
"failed to deserialize repeated values for type '{}'",
111+
value_type
112+
)
113+
})
98114
}
99115
"g-linear" => {
100116
let begin: f64 = must_translate(&mut value, map, "begin")?;
@@ -113,8 +129,54 @@ where
113129
result.push(Value::from(val));
114130
}
115131
}
116-
serde_json::from_value(result.into())
117-
.with_context(|| format!("failed to deserialize linear spaced values for type '{}'", value_type))
132+
serde_json::from_value(result.into()).with_context(|| {
133+
format!(
134+
"failed to deserialize linear spaced values for type '{}'",
135+
value_type
136+
)
137+
})
138+
}
139+
"if" => {
140+
let condition: String = must_translate(&mut value, map, "condition")?;
141+
let true_val: Value = must_translate(&mut value, map, "true")?;
142+
let false_val: Value = must_translate(&mut value, map, "false")?;
143+
144+
// Evaluate condition using fasteval
145+
let mut namespace = fasteval::StrToF64Namespace::new();
146+
// We need to check if the condition contains 'i' variable
147+
// For simplicity, assume it's a boolean expression that evaluates to non-zero for true
148+
let result = ez_eval(&condition, &mut namespace)?;
149+
let selected = if result != 0.0 { true_val } else { false_val };
150+
151+
// Parse the selected value according to its type
152+
if !selected.is_object() {
153+
serde_json::from_value::<T>(selected.clone())
154+
.with_context(|| "failed to deserialize non-object if result")
155+
} else {
156+
// If it's an object, it might be another generator
157+
Self::parse_map(map, selected)
158+
}
159+
}
160+
"time" => {
161+
// Simple time generator: produce timestamps as strings
162+
let start: String = must_translate(&mut value, map, "start")?;
163+
let end: String = must_translate(&mut value, map, "end")?;
164+
let _interval: String = must_translate(&mut value, map, "interval")?;
165+
let _format: Option<String> = if value.get("format").is_some() {
166+
Some(must_translate(&mut value, map, "format")?)
167+
} else {
168+
None
169+
};
170+
171+
// For now, just return the start and end as strings
172+
// In a real implementation, you'd parse dates and generate a series
173+
let result = vec![Value::from(start.clone()), Value::from(end.clone())];
174+
serde_json::from_value(result.into()).with_context(|| {
175+
format!(
176+
"failed to deserialize time values for type '{}'",
177+
value_type
178+
)
179+
})
118180
}
119181
_ => Err(anyhow!("unknown type `{}`", value_type)),
120182
}

0 commit comments

Comments
 (0)