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
use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH;
use ckb_hash::blake2b_256;
use ckb_types::core::ScriptHashType;
use ckb_types::packed::Script;
use ckb_types::prelude::{Builder, Entity, Pack};
use ckb_vm::Bytes;
use regex::{Captures, Regex};
use std::collections::HashMap;
use std::path::{Path, PathBuf};

pub struct Embed {
    pub data: String,
    pub path: PathBuf,
    pub type_id_dict: HashMap<String, String>,
}

impl Embed {
    pub fn new(path: PathBuf, data: String) -> Self {
        Self { data, path, type_id_dict: HashMap::new() }
    }

    pub fn replace_data(&mut self) -> &mut Self {
        let regex = Regex::new(r"\{\{ ?data (.+?) ?\}\}").unwrap();
        self.data = regex
            .replace_all(&self.data, |caps: &Captures| -> String {
                let cap1 = &caps[1];
                let path = if !Path::new(cap1).is_absolute() {
                    let root = self.path.parent().unwrap();
                    root.join(cap1)
                } else {
                    Path::new(cap1).to_path_buf()
                };
                let data = std::fs::read(&path);
                if data.is_err() {
                    panic!("Read {:?} failed : {:?}", path, data);
                }
                let data = data.unwrap();
                hex::encode(data)
            })
            .to_string();
        self
    }

    pub fn replace_hash(&mut self) -> &mut Self {
        let regex = Regex::new(r"\{\{ ?hash (.+?) ?\}\}").unwrap();
        self.data = regex
            .replace_all(&self.data, |caps: &Captures| -> String {
                let cap1 = &caps[1];
                let path = if !Path::new(cap1).is_absolute() {
                    let root = self.path.parent().unwrap();
                    root.join(cap1)
                } else {
                    Path::new(cap1).to_path_buf()
                };
                let data = std::fs::read(path).unwrap();
                hex::encode(blake2b_256(data))
            })
            .to_string();
        self
    }

    pub fn prelude_type_id(&mut self) -> &mut Self {
        let rule = Regex::new(r"\{\{ ?def_type (.+?) ?\}\}").unwrap();
        for caps in rule.captures_iter(&self.data) {
            let type_id_name = &caps[1];
            assert!(!self.type_id_dict.contains_key(type_id_name));
            let type_id_script = Script::new_builder()
                .args(Bytes::from(type_id_name.to_string()).pack())
                .code_hash(TYPE_ID_CODE_HASH.pack())
                .hash_type(ScriptHashType::Type.into())
                .build();
            let type_id_script_hash = type_id_script.calc_script_hash();
            let type_id_script_hash = format!("{:x}", type_id_script_hash);
            self.type_id_dict.insert(type_id_name.to_string(), type_id_script_hash);
        }
        self
    }

    pub fn replace_def_type(&mut self) -> &mut Self {
        let regex = Regex::new(r#""?\{\{ ?def_type (.+?) ?\}\}"?"#).unwrap();
        self.data = regex
            .replace_all(&self.data, |caps: &Captures| -> String {
                let cap1 = &caps[1];
                let type_id_script_json = ckb_jsonrpc_types::Script {
                    code_hash: TYPE_ID_CODE_HASH,
                    hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
                    args: ckb_jsonrpc_types::JsonBytes::from_vec(cap1.as_bytes().to_vec()),
                };
                return serde_json::to_string_pretty(&type_id_script_json).unwrap();
            })
            .to_string();
        self
    }

    pub fn replace_ref_type(&mut self) -> &mut Self {
        let regex = Regex::new(r"\{\{ ?ref_type (.+?) ?\}\}").unwrap();
        self.data = regex
            .replace_all(&self.data, |caps: &Captures| -> String {
                let cap1 = &caps[1];
                return self.type_id_dict[&cap1.to_string()].clone();
            })
            .to_string();
        self
    }

    pub fn replace_all(&mut self) -> String {
        self.replace_data().replace_hash().prelude_type_id().replace_def_type().replace_ref_type();
        self.data.clone()
    }
}