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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use colored::*;
use df_st_core::HasUnknown;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use serde_json::Value;
use std::collections::HashMap;

pub fn print_unknown_list_combined<T>(list: &T, prefix: &str)
where
    T: HasUnknown,
{
    let mut unknown_combiner: HashMap<String, Vec<(String, Value)>> = HashMap::new();
    list.combine_unknown(prefix, &mut unknown_combiner);
    print_unknown_list_combined_map(unknown_combiner);
}

fn print_unknown_list_combined_map(unknown_combiner: HashMap<String, Vec<(String, Value)>>) {
    for (key, mut list) in unknown_combiner {
        let mut missing_items = "".to_string();
        let mut root_key_list: Vec<String> = Vec::new();
        let re = regex::Regex::new(r"^unk_[0-9]+$").unwrap();
        // Sort the list by the key
        list.sort_unstable_by_key(|(key, _)| key.clone());
        for (missing_key, missing_object) in &list {
            // Filter out "unk_X" keys
            if re.is_match(missing_key) {
                continue;
            }
            // Print nested items
            let missing_value = match missing_object {
                Value::Object(x) => print_unknown_list_combined_value(x, 1),
                _ => "".to_string(),
            };
            // TODO: does not show if there are multiple items in one object only what type it is
            if !root_key_list.contains(&missing_key) {
                missing_items = format!("{}pub {}: {}", missing_items, missing_key, missing_value);
                root_key_list.push(missing_key.clone());
            }
        }
        if !root_key_list.is_empty() {
            let issue = df_st_core::GitIssue::<bool>{
                title: format!("Unknown value: {}{}", key, root_key_list.first().unwrap_or(&"".to_owned())),
                message: format!("There where some objects found in the import that are yet unknown to the application.\n\n\
                Path to missing object: `{}`.", key),
                labels: vec!["Unknown values".to_owned()],
                debug_info_string: Some(format!(
                    "Path to missing object: `{}`\n\
                    ```rust\n\
                    {}\n\
                    ```",
                    key,
                    missing_items.trim(),
                )),
                debug_info_json: None,
                add_steps: false,
                ask_add_files: true,
                include_backtrace: false,
            };
            warn!(
                "{} is missing:\n{}{}",
                key.yellow(),
                missing_items,
                issue.create_message(),
            );
        }
    }
}

fn print_unknown_list_combined_value(
    object: &serde_json::Map<String, Value>,
    indent_level: usize,
) -> String {
    let mut result = if object.is_empty() {
        "Option<()>,\n".to_string()
    } else {
        String::new()
    };

    let mut is_object = false;
    for (key, value) in object {
        // single value
        if key == "$value" {
            let inner_value = match value {
                Value::String(x) => x.clone(),
                _ => "".to_string(),
            };
            if inner_value.parse::<i32>().is_ok() {
                result = format!("{}Option<i32>, // Value = {}\n", result, inner_value);
            } else if inner_value == "true" || inner_value == "false" {
                result = format!("{}Option<bool>, // Value = {}\n", result, inner_value);
            } else {
                result = format!("{}Option<String>, // Value = {}\n", result, inner_value);
            }
        } else {
            // object
            is_object = true;
            let inner_object = match value {
                Value::Object(x) => print_unknown_list_combined_value(x, indent_level + 1),
                _ => "".to_string(),
            };

            result = format!(
                "{}{}pub {}: {}",
                result,
                "    ".repeat(indent_level),
                key,
                inner_object
            );
        }
    }
    if is_object {
        result = format!("Object(\n{}{})\n", result, "    ".repeat(indent_level - 1));
    }
    result
}

pub fn print_unknown_elements_list<T>(list: &T, prefix: &str)
where
    T: HasUnknown,
{
    let mut unknown_map: HashMap<String, u32> = HashMap::new();
    list.print_unknown(prefix, &mut unknown_map);
    let mut unknown_list: Vec<(&String, &u32)> = unknown_map
        .iter()
        .filter(|(key, _value)| !key.ends_with('/'))
        .collect();
    unknown_list.sort();

    let unknown_list: Vec<String> = unknown_list
        .iter()
        .map(|(key, value)| {
            let parent_key = format!("{}/", key.rsplitn(2, '/').last().unwrap());
            let parent_count = unknown_map.get(&parent_key).unwrap_or(&0);
            format!("{:>6}/{:>6} : {}", value, parent_count, key)
        })
        .collect();
    if !unknown_list.is_empty() {
        warn!("Unknown items at: {:#?}", unknown_list);
    }
}