Skip to main content

gear_utils/
lib.rs

1// Copyright (C) Gear Technologies Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4//! Useful utilities needed for testing and other stuff.
5
6use gear_core::{memory::PageBuf, pages::GearPage};
7pub use nonempty::NonEmpty;
8use parity_scale_codec::{Decode, Encode};
9use path_clean::PathClean;
10use serde::{Deserialize, Serialize};
11use std::{
12    env, fs,
13    path::Path,
14    time::{Duration, SystemTime, UNIX_EPOCH},
15};
16
17pub mod codegen;
18
19/// Trait describes a collection which can get a value by it's index.
20/// The index can be in any range, even [length(implementor), ..).
21///
22/// The key feature of the trait is that the implementor should guarantee
23/// that with under provided index there's always some value. The best way
24/// to do that is to implement a trait for a guaranteed not empty type.
25pub trait RingGet<V> {
26    /// Returns with a guarantee a value under `index`.
27    fn ring_get(&self, index: usize) -> &V;
28}
29
30impl<V> RingGet<V> for NonEmpty<V> {
31    fn ring_get(&self, index: usize) -> &V {
32        // Guaranteed to have value, because index is in the range of [0; self.len()).
33        self.get(index % self.len()).expect("guaranteed to be some")
34    }
35}
36
37/// Returns time elapsed since [`UNIX_EPOCH`] in milliseconds.
38pub fn now_millis() -> u64 {
39    now_duration().as_millis() as u64
40}
41
42/// Returns time elapsed since [`UNIX_EPOCH`] in microseconds.
43pub fn now_micros() -> u128 {
44    now_duration().as_micros()
45}
46
47/// Returns [`Duration`] from [`UNIX_EPOCH`] until now.
48pub fn now_duration() -> Duration {
49    SystemTime::now()
50        .duration_since(UNIX_EPOCH)
51        .expect("Internal error: current time before UNIX Epoch")
52}
53
54/// Initialize a simple logger from env.
55///
56/// Does show:
57/// - level
58/// - timestamp
59/// - module
60///
61/// Does not show
62/// - module path
63pub fn init_default_logger() {
64    let _ = tracing_subscriber::fmt::try_init();
65}
66
67/// Stores one memory page dump as the hex string.
68#[derive(Serialize, Deserialize)]
69pub struct MemoryPageDump {
70    page: u32,
71    data: Option<String>,
72}
73
74impl MemoryPageDump {
75    pub fn new(page_number: GearPage, page_data: PageBuf) -> MemoryPageDump {
76        let mut data_vec = vec![];
77        page_data.encode_to(&mut data_vec);
78        let data = data_vec
79            .iter()
80            .any(|&byte| byte != 0)
81            .then(|| hex::encode(data_vec));
82
83        MemoryPageDump {
84            page: page_number.into(),
85            data,
86        }
87    }
88
89    pub fn into_gear_page(self) -> (GearPage, PageBuf) {
90        let page_buf = if let Some(page_hex) = self.data {
91            let data = hex::decode(page_hex).expect("Unexpected memory page data encoding");
92            PageBuf::decode(&mut &*data).expect("Invalid PageBuf data found")
93        } else {
94            PageBuf::new_zeroed()
95        };
96        (
97            GearPage::try_from(self.page)
98                .unwrap_or_else(|_| panic!("Couldn't decode GearPage from u32: {}", self.page)),
99            page_buf,
100        )
101    }
102}
103
104/// Stores all program's page dumps and it's balance.
105#[derive(Serialize, Deserialize, Default)]
106pub struct ProgramMemoryDump {
107    pub balance: u128,
108    pub reserved_balance: u128,
109    pub pages: Vec<MemoryPageDump>,
110}
111
112impl ProgramMemoryDump {
113    pub fn save_to_file(&self, path: impl AsRef<Path>) {
114        let path = env::current_dir()
115            .expect("Unable to get root directory of the project")
116            .join(path)
117            .clean();
118
119        if let Some(parent) = path.parent() {
120            fs::create_dir_all(parent)
121                .unwrap_or_else(|_| panic!("Couldn't create folder {}", parent.display()));
122        }
123
124        let data =
125            serde_json::to_string(&self).expect("Failed to serialize ProgramMemoryDump to JSON");
126
127        fs::write(&path, data).unwrap_or_else(|_| panic!("Failed to write file {path:?}"));
128    }
129
130    pub fn load_from_file(path: impl AsRef<Path>) -> ProgramMemoryDump {
131        let path = env::current_dir()
132            .expect("Unable to get root directory of the project")
133            .join(path)
134            .clean();
135
136        let json =
137            fs::read_to_string(&path).unwrap_or_else(|_| panic!("Failed to read file {path:?}"));
138
139        serde_json::from_str(&json)
140            .unwrap_or_else(|_| panic!("Failed to deserialize {path:?} as ProgramMemoryDump"))
141    }
142}