///|
fn kern_spec() -> FunctionSpec {
  FunctionSpec::make(
    ["\\kern", "\\mkern", "\\hskip", "\\mskip"],
    1,
    arg_types=[SizeArg],
    primitive=true,
    allowed_in_text=true,
    handler=kern_handler,
  )
}

///|
fn kern_dimension(
  args : Array[ParseNode],
  func_name : String,
) -> Measurement raise ParseFailure {
  guard require_function_arg(args, 0, func_name) is Size(value~, ..) else {
    raise InternalInvariant(message="Expected size argument for " + func_name)
  }
  value
}

///|
fn is_math_spacing_function(func_name : String) -> Bool {
  func_name == "\\mkern" || func_name == "\\mskip"
}

///|
fn kern_handler(
  context : FunctionContext,
  args : Array[ParseNode],
  _opt_args : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
  let dimension = kern_dimension(args, context.func_name)
  let uses_mu = dimension.unit == "mu"
  if is_math_spacing_function(context.func_name) {
    if !uses_mu {
      (context.report_nonstrict)(
        "mathVsTextUnits",
        "LaTeX's " +
        context.func_name +
        " supports only mu units, not " +
        dimension.unit +
        " units",
      )
    }
    if context.mode != Math {
      (context.report_nonstrict)(
        "mathVsTextUnits",
        "LaTeX's " + context.func_name + " works only in math mode",
      )
    }
  } else if uses_mu {
    (context.report_nonstrict)(
      "mathVsTextUnits",
      "LaTeX's " + context.func_name + " doesn't support mu units",
    )
  }
  Kern(mode=context.mode, dimension~)
}