jsonpiler 0.10.4

a Json syntax programming language for Windows
Documentation
use super::utility::*;
use crate::prelude::*;
impl Pos<Parser> {
  pub(crate) fn format_block(
    &mut self,
    out: &mut String,
    object: &[KeyVal],
    obj_size: u32,
    indentation: u32,
  ) -> Option<()> {
    for (obj_idx, key_val) in object.iter().enumerate() {
      let size = self.sizeof_key_val(key_val)? + indentation * 2;
      let (key, val) = key_val;
      if obj_idx != 0 {
        if LINE_MAX > obj_size {
          out.push(';');
        }
        self.comment_sep(out, obj_size, val.pos.offset, indentation);
      }
      if &key.val == "$"
        && let Str(Lit(variable)) = &val.val
      {
        out.push_str(variable);
      } else if let Some(value) = value(key, val) {
        self.format_json(out, value, indentation)?;
      } else {
        match key.pos.info {
          INFO_NONE | INFO_FUNC => {
            self.format_type_func(out, key, val, size, indentation)?;
          }
          INFO_KEY_VAL => {
            out.push_str(&key.val);
            out.push_str(": ");
            if val.val.operator().is_some() {
              out.push_str("{ ");
              self.format_json(out, val, indentation)?;
              out.push_str(" }");
            } else {
              self.format_json(out, val, indentation)?;
            }
          }
          INFO_OP => self.format_type_op(out, key, val, size, indentation)?,
        }
      }
    }
    Some(())
  }
  pub(crate) fn format_object(
    &mut self,
    out: &mut String,
    size: u32,
    pos: Position,
    object: &[KeyVal],
    indentation: u32,
  ) -> Option<()> {
    if object.is_empty() {
      out.push_str("{}");
      return Some(());
    }
    if object.len() == 1 && object[0].0.pos.info != INFO_KEY_VAL {
      return self.format_block(out, object, size, indentation);
    }
    out.push('{');
    self.comment_sep(out, size, pos.offset + 1, indentation);
    self.format_block(out, object, size, indentation)?;
    self.comment_sep(out, size, pos.end() - 1, indentation.saturating_sub(1));
    out.push('}');
    Some(())
  }
  pub(crate) fn format_type_func(
    &mut self,
    out: &mut String,
    key: &Pos<String>,
    val: &Pos<Json>,
    size: u32,
    indentation: u32,
  ) -> Option<()> {
    out.push_str(&key.val);
    out.push('(');
    let val_size = (indentation + 1) * 2 + self.sizeof_json(val)?;
    let is_block = val.val.is_block()
      || if let Array(Lit(array)) = &val.val
        && array.len() == 1
        && array[0].val.is_block()
      {
        true
      } else {
        false
      };
    let is_single_if = if let Array(Lit(array)) = &val.val
      && array.len() == 2
      && array[0].val.as_type() != ArrayT
    {
      matches!(key.val.as_ref(), "if")
    } else {
      false
    };
    let is_while = key.val == "while";
    let is_define = key.val == "define";
    self.comment(
      out,
      val.pos.offset + 1,
      indentation + u32::from(val_size < LINE_MAX || !is_block),
      LINE_MAX <= size && !is_define && !is_while && !is_single_if,
    );
    let mut sig = 0;
    match val {
      Pos { val: Array(Lit(args)), .. } => {
        for (idx, item) in args.iter().enumerate() {
          if idx == 0 {
            self.format_json(out, item, indentation + 1)?;
            continue;
          }
          out.push(',');
          if is_define && idx < 3 && sig < LINE_MAX {
            out.push(' ');
            self.format_json(out, item, indentation + 1)?;
            sig += self.sizeof_json(item)?;
          } else {
            self.format_json_sep(out, size, item.pos.offset + 1, item, indentation)?;
          }
        }
      }
      arg => self.format_json(out, arg, indentation + 1)?,
    }
    self.comment(out, val.pos.end() - 1, indentation, LINE_MAX <= size);
    out.push(')');
    Some(())
  }
  pub(crate) fn format_type_op(
    &mut self,
    out: &mut String,
    key: &Pos<String>,
    val: &Pos<Json>,
    size: u32,
    indentation: u32,
  ) -> Option<()> {
    let Pos { val: Array(Lit(args)), .. } = val else { return None };
    for (idx, item) in args.iter().enumerate() {
      if idx != 0 {
        out.push(' ');
        out.push_str(&key.val);
        out.push(' ');
      }
      if ASSIGN_OP.contains(&key.val.as_ref()) {
        self.format_json(out, item, indentation)?;
      } else if LINE_MAX <= size || item.val.needs_braces(&key.val) {
        out.push('{');
        self.format_json_sep(out, size, item.pos.offset + 1, item, indentation)?;
        self.comment_sep(out, size, item.pos.end() - 1, indentation);
        out.push('}');
      } else {
        self.format_json(out, item, indentation + 1)?;
      }
    }
    Some(())
  }
  #[expect(clippy::cast_possible_truncation)]
  pub(crate) fn sizeof_key_val(&self, (key, val): &KeyVal) -> Option<u32> {
    if &key.val == "$"
      && let Some(variable) = &val.val.as_str()
    {
      Some(variable.len() as u32)
    } else if let Some(value) = value(key, val) {
      self.sizeof_json(value)
    } else {
      Some(match key.pos.info {
        INFO_NONE | INFO_FUNC => {
          let mut acc = key.val.len() as u32 + self.sizeof_json(val)?;
          if let Array(Lit(array)) = &val.val {
            if array.is_empty() {
              acc -= 2;
            }
          } else {
            acc += 2;
          }
          acc
        }
        INFO_KEY_VAL => key.val.len() as u32 + 2 + self.sizeof_json(val)?,
        INFO_OP => {
          let Array(Lit(args)) = &val.val else { return None };
          let mut acc = ((key.val.len() + 2) * (args.len().saturating_sub(1)) + 2) as u32;
          for item in args {
            if item.val.needs_braces(&key.val) {
              acc += 4;
            }
            acc += self.sizeof_json(item)?;
          }
          acc
        }
      })
    }
  }
}
impl Json {
  pub(crate) fn is_block(&self) -> bool {
    if let Object(Lit(obj)) = &self
      && obj.len() != 1
    {
      true
    } else {
      false
    }
  }
  pub(crate) fn needs_braces(&self, parent: &str) -> bool {
    let Some(child) = self.operator() else {
      return false;
    };
    let child_prec = op_precedence(&child.val);
    let parent_prec = op_precedence(parent);
    match (child_prec, parent_prec) {
      (None, None | Some(_)) => true,
      (Some(_), None) => false,
      (Some(ch), Some(pa)) => {
        ch <= pa
          && parent != child.val
          && !(parent == "-" && child.val == "+")
          && !(parent == "+" && child.val == "-")
      }
    }
  }
  pub(crate) fn operator(&self) -> Option<&Pos<String>> {
    if let Object(Lit(obj)) = &self
      && obj.len() == 1
      && obj[0].0.pos.info == INFO_OP
    {
      Some(&obj[0].0)
    } else {
      None
    }
  }
}
pub(crate) fn value<'a>(key: &Pos<String>, val: &'a Pos<Json>) -> Option<&'a Pos<Json>> {
  if &key.val == "value"
    && let Array(Lit(array)) = &val.val
    && array.len() == 1
  {
    Some(&array[0])
  } else {
    None
  }
}