parse_book_source/source/rule/
rule_content.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::HttpClient;
use crate::Result;
use crate::Variables;
use crate::{utils::JsonData, ParseError};
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub enum RuleContent {
    #[serde(rename_all = "camelCase")]
    More {
        content: String,
        next_content_url: String,
        start: usize,
        end: String,
    },
    One {
        content: String,
    },
}

impl TryFrom<&RuleContent> for JsonRuleContent {
    type Error = ParseError;

    fn try_from(value: &RuleContent) -> std::result::Result<Self, Self::Error> {
        match value {
            RuleContent::More {
                content,
                next_content_url,
                start,
                end,
            } => Ok(JsonRuleContent::More {
                content: content.try_into()?,
                next_content_url: next_content_url.try_into()?,
                start: *start,
                end: end.try_into()?,
            }),
            RuleContent::One { content } => Ok(JsonRuleContent::One {
                content: content.try_into()?,
            }),
        }
    }
}

impl TryFrom<RuleContent> for JsonRuleContent {
    type Error = ParseError;
    fn try_from(value: RuleContent) -> std::result::Result<Self, Self::Error> {
        Self::try_from(&value)
    }
}

#[derive(Debug, Clone)]
pub enum JsonRuleContent {
    More {
        content: JsonData,
        next_content_url: JsonData,
        start: usize,
        end: JsonData,
    },
    One {
        content: JsonData,
    },
}

impl JsonRuleContent {
    pub async fn parse_content(
        &self,
        data: &Value,
        variables: &mut Variables,
        client: &HttpClient,
    ) -> Result<String> {
        match self {
            JsonRuleContent::One { content } => Ok(content.parse_data(data, variables)?),
            JsonRuleContent::More {
                content,
                start,
                end,
                next_content_url,
            } => {
                let mut content_str = content.parse_data(data, variables)?;
                let end = end.parse_data(data, variables)?.parse::<usize>()?;
                for i in *start..=end {
                    variables.insert("index", i);

                    let url = &next_content_url.parse_data(data, variables)?;

                    let res: Value = client.get(url).await?.json().await?;
                    content_str += &content.parse_data(&res, variables)?;
                }

                Ok(content_str)
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_rule_content() -> Result<()> {
        let json = json!({
            "nextContentUrl": "http://www.baidu.com",
            "start": 1,
            "end": "2",
            "content":"hello world",
        });

        let rule_content: RuleContent = serde_json::from_value(json)?;
        assert!(matches!(rule_content, RuleContent::More { .. }));
        Ok(())
    }
}