toml-input 0.1.4

# A library to generate toml text with clear options and comments
Documentation
use toml::{Value as TomlValue, map::Map};

use crate::util;
#[derive(Debug, Clone, Default)]
pub struct BlockValue {
    pub key: String,
    pub tag: String,
    pub value: Option<TomlValue>,
    pub array_index: Option<usize>,
}

#[derive(Debug, Clone, Default)]
pub struct PrimValue {
    pub tag: String,
    pub raw: Option<TomlValue>,
}

impl PrimValue {
    pub fn new(raw: TomlValue) -> Self {
        PrimValue {
            raw: Some(raw),
            ..Default::default()
        }
    }
    pub fn flatten(self) -> BlockValue {
        let PrimValue { tag, raw } = self;
        BlockValue {
            tag,
            value: raw,
            ..Default::default()
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct ArrayValue {
    pub values: Vec<Value>,
}

impl ArrayValue {
    pub fn into_prim(self) -> PrimValue {
        let mut raws = Vec::new();
        for value in self.values {
            let PrimValue { raw, .. } = value.into_prim();
            if let Some(raw) = raw {
                raws.push(raw);
            }
        }
        PrimValue {
            tag: String::new(),
            raw: Some(TomlValue::Array(raws)),
        }
    }

    pub fn flatten(self) -> Vec<BlockValue> {
        let ArrayValue { values, .. } = self;
        let mut blocks = Vec::new();
        for (i, value) in values.into_iter().enumerate() {
            let tmp = value.flatten();
            for mut block in tmp {
                block.array_index = Some(i);
                blocks.push(block);
            }
        }
        blocks
    }
}

#[derive(Debug, Clone, Default)]
pub struct TableValue {
    pub fields: Vec<FieldValue>,
}

impl TableValue {
    pub fn into_prim(self) -> PrimValue {
        let mut map = Map::new();
        for field in self.fields {
            for (key, value) in field.into_map() {
                map.insert(key, value);
            }
        }
        PrimValue {
            tag: String::new(),
            raw: Some(TomlValue::Table(map)),
        }
    }

    pub fn flatten(self) -> Vec<BlockValue> {
        let TableValue { fields, .. } = self;
        let mut values = Vec::new();
        for field in fields {
            values.append(&mut field.flatten());
        }
        values
    }
}

#[derive(Debug, Clone)]
pub enum Value {
    Prim(PrimValue),
    Array(ArrayValue),
    Table(TableValue),
}

impl Default for Value {
    fn default() -> Self {
        Value::Prim(PrimValue::default())
    }
}

impl Value {
    pub fn new_prim(raw: TomlValue) -> Self {
        let prim = PrimValue::new(raw);
        Value::Prim(prim)
    }

    pub fn into_prim(self) -> PrimValue {
        match self {
            Value::Prim(prim) => prim,
            Value::Array(array) => array.into_prim(),
            Value::Table(table) => table.into_prim(),
        }
    }

    pub fn is_prim(&self) -> bool {
        matches!(self, Value::Prim(_))
    }

    pub fn is_array(&self) -> bool {
        matches!(self, Value::Array(_))
    }

    pub fn is_table(&self) -> bool {
        matches!(self, Value::Table(_))
    }

    pub fn flatten(self) -> Vec<BlockValue> {
        match self {
            Value::Prim(prim) => vec![prim.flatten()],
            Value::Array(array) => array.flatten(),
            Value::Table(table) => table.flatten(),
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct FieldValue {
    pub ident: String,
    pub value: Value,
    pub flat: bool,
}

impl FieldValue {
    pub fn into_map(self) -> Map<String, TomlValue> {
        let FieldValue { ident, value, flat } = self;
        let PrimValue { raw, .. } = value.into_prim();
        let mut map = Map::new();
        if flat {
            if let Some(TomlValue::Table(map)) = raw {
                return map;
            }
        }
        if let Some(raw) = raw {
            map.insert(ident, raw);
        }
        map
    }
    pub fn flatten(self) -> Vec<BlockValue> {
        let FieldValue { ident, value, flat } = self;
        let mut blocks = value.flatten();
        if !flat {
            for block in &mut blocks {
                util::increase_key(&mut block.key, &ident);
            }
        }
        blocks
    }
}