///|
/// What a definition's tokens parsed as. `Skipped` is a run of tokens the
/// parser reported and skipped (recovery per definition).
pub(all) enum DefinitionKind {
  Import
  Constant
  Function
  CustomType
  TypeAlias
  Skipped
} derive(Eq, Debug, Hash)

///|
/// The tokens of one definition, with their trivia. `name` is the module
/// path of an import, the name of a constant, function or type, or `None`
/// for a skipped run or an anonymous head. `span` runs from the first
/// token's start to the last token's end (trivia not included); `0..0`
/// when there is no token.
pub(all) struct DefinitionCst {
  kind : DefinitionKind
  name : String?
  tokens : ArrayView[@scanner.Token]
  span : @span.SrcSpan
} derive(Eq, Debug)

///|
pub fn DefinitionCst::new(
  kind~ : DefinitionKind,
  name~ : String?,
  tokens~ : ArrayView[@scanner.Token],
) -> DefinitionCst {
  let span = if tokens.length() == 0 {
    @span.SrcSpan::new(0, 0)
  } else {
    @span.SrcSpan::new(
      tokens[0].span.start,
      tokens[tokens.length() - 1].span.end,
    )
  }
  { kind, name, tokens, span, }
}

///|
/// The text of the definition's tokens with their trivia.
pub fn DefinitionCst::to_source(self : DefinitionCst) -> String {
  let sb = StringBuilder()
  for t in self.tokens {
    sb.write_string(t.to_source())
  }
  sb.to_string()
}

///|
/// Every token of a module, grouped per definition. `definitions` tile
/// `tokens` in order; `trivia` is the stream's trivia for a text with no
/// token.
///
/// ```mbt check
/// test {
///   let stream = @scanner.tokenize_text("const x = 1\n")
///   let d = @cst.DefinitionCst::new(
///     kind=Constant,
///     name=Some("x"),
///     tokens=stream.tokens,
///   )
///   let cst = @cst.ModuleCst::new(stream, [d])
///   inspect(cst.to_source(), content="const x = 1\n")
/// }
/// ```
pub(all) struct ModuleCst {
  tokens : ArrayView[@scanner.Token]
  trivia : ArrayView[@scanner.Trivia]
  definitions : ArrayView[DefinitionCst]
} derive(Eq, Debug)

///|
pub fn ModuleCst::new(
  stream : @scanner.TokenStream,
  definitions : Array[DefinitionCst],
) -> ModuleCst {
  { tokens: stream.tokens, trivia: stream.trivia, definitions: definitions[:], }
}

///|
/// The scanned text, byte for byte.
pub fn ModuleCst::to_source(self : ModuleCst) -> String {
  let sb = StringBuilder()
  for t in self.trivia {
    sb.write_string(t.text())
  }
  for token in self.tokens {
    sb.write_string(token.to_source())
  }
  sb.to_string()
}