yerba 0.4.2

YAML Editing and Refactoring with Better Accuracy
use crate::selector::{Selector, SelectorSegment};

pub fn yaml_to_json(value: &serde_yaml::Value) -> serde_json::Value {
  match value {
    serde_yaml::Value::Null => serde_json::Value::Null,
    serde_yaml::Value::Bool(boolean) => serde_json::Value::Bool(*boolean),

    serde_yaml::Value::Number(number) => {
      if let Some(integer) = number.as_i64() {
        serde_json::Value::Number(integer.into())
      } else if let Some(float) = number.as_f64() {
        serde_json::json!(float)
      } else {
        serde_json::Value::String(number.to_string())
      }
    }

    serde_yaml::Value::String(string) => serde_json::Value::String(string.clone()),

    serde_yaml::Value::Sequence(sequence) => serde_json::Value::Array(sequence.iter().map(yaml_to_json).collect()),

    serde_yaml::Value::Mapping(mapping) => {
      let mut map = serde_json::Map::new();

      for (key, yaml_value) in mapping {
        let json_key = match key {
          serde_yaml::Value::String(string) => string.clone(),
          _ => format!("{:?}", key),
        };

        map.insert(json_key, yaml_to_json(yaml_value));
      }

      serde_json::Value::Object(map)
    }

    serde_yaml::Value::Tagged(tagged) => yaml_to_json(&tagged.value),
  }
}

pub fn resolve_select_field(value: &serde_yaml::Value, field: &str) -> serde_json::Value {
  let parsed = Selector::parse(field);
  let segments = parsed.segments();

  if segments.len() == 1 {
    if let SelectorSegment::Key(key) = &segments[0] {
      if let serde_yaml::Value::Mapping(map) = value {
        for (map_key, yaml_value) in map {
          if let serde_yaml::Value::String(key_string) = map_key {
            if key_string == key {
              return yaml_to_json(yaml_value);
            }
          }
        }
      }

      return serde_json::Value::Null;
    }
  }

  let mut current_values = vec![value.clone()];

  for segment in segments {
    let mut next_values = Vec::new();

    for current in &current_values {
      match segment {
        SelectorSegment::AllItems => {
          if let serde_yaml::Value::Sequence(sequence) = current {
            next_values.extend(sequence.iter().cloned());
          }
        }

        SelectorSegment::Index(index) => {
          if let serde_yaml::Value::Sequence(sequence) = current {
            if let Some(item) = sequence.get(*index) {
              next_values.push(item.clone());
            }
          }
        }

        SelectorSegment::Key(key) => {
          if let serde_yaml::Value::Mapping(map) = current {
            for (map_key, yaml_value) in map {
              if let serde_yaml::Value::String(key_string) = map_key {
                if key_string == key {
                  next_values.push(yaml_value.clone());
                }
              }
            }
          }
        }
      }
    }

    current_values = next_values;
  }

  let used_all_items = parsed.segments().iter().any(|s| matches!(s, SelectorSegment::AllItems));

  if current_values.is_empty() {
    if used_all_items {
      serde_json::Value::Array(Vec::new())
    } else {
      serde_json::Value::Null
    }
  } else if current_values.len() == 1 && !used_all_items {
    yaml_to_json(&current_values[0])
  } else {
    serde_json::Value::Array(current_values.iter().map(yaml_to_json).collect())
  }
}

pub fn select_field_key(field: &str) -> String {
  let parsed = Selector::parse(field);

  parsed
    .segments()
    .iter()
    .find_map(|segment| if let SelectorSegment::Key(key) = segment { Some(key.clone()) } else { None })
    .unwrap_or_else(|| field.to_string())
}