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
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use move_core_types::{
    account_address::AccountAddress,
    ident_str,
    identifier::IdentStr,
    language_storage::{StructTag, TypeTag},
};
use serde::{Deserialize, Serialize};

use crate::{IOTA_FRAMEWORK_ADDRESS, MoveTypeTagTrait, base_types::EpochId, id::UID};

pub const CONFIG_MODULE_NAME: &IdentStr = ident_str!("config");
pub const CONFIG_STRUCT_NAME: &IdentStr = ident_str!("Config");
pub const SETTING_STRUCT_NAME: &IdentStr = ident_str!("Setting");
pub const SETTING_DATA_STRUCT_NAME: &IdentStr = ident_str!("SettingData");
pub const RESOLVED_IOTA_CONFIG: (&AccountAddress, &IdentStr, &IdentStr) = (
    &IOTA_FRAMEWORK_ADDRESS,
    CONFIG_MODULE_NAME,
    CONFIG_STRUCT_NAME,
);

/// Rust representation of the Move type 0x2::config::Config.
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
    pub id: UID,
}

/// Rust representation of the Move type 0x2::config::Setting.
#[derive(Debug, Serialize, Deserialize)]
pub struct Setting<V> {
    pub data: Option<SettingData<V>>,
}

/// Rust representation of the Move type 0x2::config::SettingData.
#[derive(Debug, Serialize, Deserialize)]
pub struct SettingData<V> {
    pub newer_value_epoch: u64,
    pub newer_value: Option<V>,
    pub older_value_opt: Option<V>,
}

impl Config {
    pub fn type_() -> StructTag {
        StructTag {
            address: IOTA_FRAMEWORK_ADDRESS,
            module: CONFIG_MODULE_NAME.to_owned(),
            name: CONFIG_STRUCT_NAME.to_owned(),
            type_params: vec![],
        }
    }
}

pub fn setting_type(value_tag: TypeTag) -> StructTag {
    StructTag {
        address: IOTA_FRAMEWORK_ADDRESS,
        module: CONFIG_MODULE_NAME.to_owned(),
        name: SETTING_STRUCT_NAME.to_owned(),
        type_params: vec![value_tag],
    }
}

impl MoveTypeTagTrait for Config {
    fn get_type_tag() -> TypeTag {
        TypeTag::Struct(Box::new(Config::type_()))
    }
}

impl<V: MoveTypeTagTrait> MoveTypeTagTrait for Setting<V> {
    fn get_type_tag() -> TypeTag {
        TypeTag::Struct(Box::new(setting_type(V::get_type_tag())))
    }
}

pub fn is_setting(tag: &StructTag) -> bool {
    let StructTag {
        address,
        module,
        name,
        type_params,
    } = tag;
    *address == IOTA_FRAMEWORK_ADDRESS
        && module.as_ident_str() == CONFIG_MODULE_NAME
        && name.as_ident_str() == SETTING_STRUCT_NAME
        && type_params.len() == 1
}

impl<V> Setting<V> {
    /// Calls `SettingData::read_value` on the setting's data.
    /// The `data` should never be `None`, but for safety, this method returns
    /// `None` if it is.
    pub fn read_value(&self, cur_epoch: Option<EpochId>) -> Option<&V> {
        self.data.as_ref()?.read_value(cur_epoch)
    }
}

impl<V> SettingData<V> {
    /// Reads the value of the setting, giving `newer_value` if the current
    /// epoch is greater than `newer_value_epoch`, and `older_value_opt`
    /// otherwise. If `cur_epoch` is `None`, the `newer_value` is always
    /// returned.
    pub fn read_value(&self, cur_epoch: Option<EpochId>) -> Option<&V> {
        let use_newer_value = match cur_epoch {
            Some(cur_epoch) => cur_epoch > self.newer_value_epoch,
            None => true,
        };
        if use_newer_value {
            self.newer_value.as_ref()
        } else {
            self.older_value_opt.as_ref()
        }
    }
}