yerba 0.5.1

YAML Editing and Refactoring with Better Accuracy
use super::*;

impl Document {
  pub fn validate_schema(&self, schema: &serde_json::Value, items: bool, selector: Option<&str>) -> Vec<crate::schema::ValidationError> {
    let path = selector.unwrap_or("");
    let has_wildcard = crate::selector::Selector::parse(path).has_wildcard();

    let value = match self.get_value(path) {
      Some(value) => crate::json::yaml_to_json(&value),
      None => return vec![],
    };

    let validate_items = items || has_wildcard;

    let mut errors = if validate_items {
      match value.as_array() {
        Some(array) => crate::schema::validate_array(array, schema),
        None if value.is_null() => vec![crate::schema::ValidationError {
          path: String::new(),
          message: "expected an array but document is empty or not a sequence".to_string(),
          item_label: None,
          line: None,
        }],
        None => crate::schema::validate_array(std::slice::from_ref(&value), schema),
      }
    } else {
      if value.is_null() {
        return vec![];
      }

      crate::schema::validate_value(&value, schema)
    };

    let source = self.to_string();

    for error in &mut errors {
      if !error.path.is_empty() {
        let selector = json_pointer_to_selector(&error.path);

        if let Ok(node) = self.navigate(&selector) {
          let offset: usize = node.text_range().start().into();
          let line = source[..offset].chars().filter(|c| *c == '\n').count() + 1;

          error.line = Some(line);
        }
      }
    }

    errors
  }
}

fn json_pointer_to_selector(pointer: &str) -> String {
  pointer
    .strip_prefix('/')
    .unwrap_or(pointer)
    .split('/')
    .map(|segment| {
      if let Ok(index) = segment.parse::<usize>() {
        format!("[{}]", index)
      } else {
        segment.to_string()
      }
    })
    .fold(String::new(), |mut path, segment| {
      if !path.is_empty() && !segment.starts_with('[') {
        path.push('.');
      }

      path.push_str(&segment);
      path
    })
}