///|
fn normalize_line_ending(line : String) -> String {
  if line.has_suffix("\r\n") {
    line[:line.length() - 2].to_owned()
  } else if line.has_suffix("\n") {
    line[:line.length() - 1].to_owned()
  } else {
    line
  }
}

///|
fn decode_read_result(
  status : Int,
  bytes : Bytes,
  error_code : Int,
) -> String? raise ReadlineError {
  match status {
    0 => {
      let line = @utf8.decode(bytes) catch { _ => raise InvalidUtf8 }
      Some(normalize_line_ending(line))
    }
    1 => None
    2 => raise Busy
    3 => raise EmbeddedNul
    5 => raise Interrupted
    _ => raise ReadFailed(error_code)
  }
}

///|
/// 同步读取并编辑一行文本。
///
/// 返回 `Some(line)` 表示用户提交了一行,返回 `None` 只表示正常 EOF。直接回车返回
/// `Some("")`,不会与 EOF 混淆。返回文本由 MoonBit 独立持有,不包含末尾换行符。
/// 此方法不会自动将返回值加入 History。
///
/// **Parameters:**
///
/// - `prompt`:本次读取期间显示的提示符,默认为空字符串,不会成为长期配置。
///
/// **Errors:**
///
/// - `prompt` 包含 NUL,或底层行数据包含 NUL 时抛出 `EmbeddedNul`。
/// - 底层行数据不是合法 UTF-8 时抛出 `InvalidUtf8`。
/// - 同一进程已有活跃的同步读取,或所连接的 History 正在使用时抛出 `Busy`。
/// - 用户按下 Ctrl-C 中断本次输入时抛出 `Interrupted`;未提交输入会被丢弃,editor
///   可以继续读取。
/// - 发生读取错误时抛出 `ReadFailed`,并携带 C 边界立即保存的 OS error code;零表示
///   没有可靠错误码。
///
/// **Lifecycle:**
///
/// prompt 及底层返回缓冲区只在本次调用期间使用。方法返回或抛错前会复制结果,并恢复
/// editor、History、locale、signal handler、signal mask 和进程级读取 guard。
///
/// **Side effects:**
///
/// 此方法会阻塞并暂时接管标准终端的行编辑状态。
///
/// **Thread safety:**
///
/// 多个 `LineEditor` 可以共存和顺序读取,但整个进程同一时刻只允许一个活跃读取;冲突
/// 不等待,直接抛出 `Busy`。
///
/// **Examples:**
///
/// ```mbt nocheck
/// let editor = LineEditor::open()
/// match editor.read_line(prompt=">> ") {
///   Some(line) => println(line)
///   None => println("EOF")
/// }
/// ```
pub fn LineEditor::read_line(
  self : LineEditor,
  prompt? : StringView = "",
) -> String? raise ReadlineError {
  let bytes = self.owner.read(@utf8.encode(prompt))
  decode_read_result(self.owner.read_status(), bytes, self.owner.read_errno())
}