// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { convertRowToObject, defaultReadOptions, parseRecord } from "./_io.ts";
import { TextDelimiterStream } from "jsr:/@std/streams@^0.224.4/text-delimiter-stream";
class StreamLineReader {
  #reader;
  #done = false;
  constructor(reader){
    this.#reader = reader;
  }
  async readLine() {
    const { value, done } = await this.#reader.read();
    if (done) {
      this.#done = true;
      return null;
    } else {
      // NOTE: Remove trailing CR for compatibility with golang's `encoding/csv`
      return stripLastCR(value);
    }
  }
  isEOF() {
    return this.#done;
  }
  cancel() {
    this.#reader.cancel();
  }
}
function stripLastCR(s) {
  return s.endsWith("\r") ? s.slice(0, -1) : s;
}
/**
 * Read data from a CSV-encoded stream or file. Provides an auto/custom mapper
 * for columns.
 *
 * A `CsvParseStream` expects input conforming to
 * {@link https://www.rfc-editor.org/rfc/rfc4180.html | RFC 4180}.
 *
 * @example Usage
 * ```ts no-assert
 * import { CsvParseStream } from "@std/csv/csv-parse-stream";
 *
 * const source = ReadableStream.from([
 *   "name,age",
 *   "Alice,34",
 *   "Bob,24",
 *   "Charlie,45",
 * ]);
 * const parts = source.pipeThrough(new CsvParseStream());
 * ```
 *
 * @typeParam T The type of options for the stream.
 */ export class CsvParseStream {
  #readable;
  #options;
  #lineReader;
  #lines;
  #lineIndex = 0;
  #isFirstRow = true;
  #headers = [];
  /** Construct a new instance.
   *
   * @example Usage
   * ```ts no-assert
   * import { CsvParseStream } from "@std/csv/csv-parse-stream";
   *
   * const source = ReadableStream.from([
   *   "name,age",
   *   "Alice,34",
   *   "Bob,24",
   *   "Charlie,45",
   * ]);
   * const parts = source.pipeThrough(new CsvParseStream());
   * ```
   *
   * @param options Options for the stream.
   */ constructor(options){
    this.#options = {
      ...defaultReadOptions,
      ...options
    };
    this.#lines = new TextDelimiterStream("\n");
    this.#lineReader = new StreamLineReader(this.#lines.readable.getReader());
    this.#readable = new ReadableStream({
      pull: (controller)=>this.#pull(controller),
      cancel: ()=>this.#lineReader.cancel()
    });
  }
  async #pull(controller) {
    const line = await this.#lineReader.readLine();
    if (line === "") {
      // Found an empty line
      this.#lineIndex++;
      return this.#pull(controller);
    }
    if (line === null) {
      // Reached to EOF
      controller.close();
      this.#lineReader.cancel();
      return;
    }
    const record = await parseRecord(line, this.#lineReader, this.#options, this.#lineIndex);
    if (record === null) {
      controller.close();
      this.#lineReader.cancel();
      return;
    }
    if (this.#isFirstRow) {
      this.#isFirstRow = false;
      if (this.#options.skipFirstRow || this.#options.columns) {
        this.#headers = [];
        if (this.#options.skipFirstRow) {
          const head = record;
          this.#headers = head;
        }
        if (this.#options.columns) {
          this.#headers = this.#options.columns;
        }
      }
      if (this.#options.skipFirstRow) {
        return this.#pull(controller);
      }
    }
    this.#lineIndex++;
    if (record.length > 0) {
      if (this.#options.skipFirstRow || this.#options.columns) {
        controller.enqueue(convertRowToObject(record, this.#headers, this.#lineIndex));
      } else {
        controller.enqueue(record);
      }
    } else {
      return this.#pull(controller);
    }
  }
  /**
   * The instance's {@linkcode ReadableStream}.
   *
   * @example Usage
   * ```ts no-assert
   * import { CsvParseStream } from "@std/csv/csv-parse-stream";
   *
   * const source = ReadableStream.from([
   *   "name,age",
   *   "Alice,34",
   *   "Bob,24",
   *   "Charlie,45",
   * ]);
   * const parseStream = new CsvParseStream();
   * const parts = source.pipeTo(parseStream.writable);
   * for await (const part of parseStream.readable) {
   *   console.log(part);
   * }
   * ```
   *
   * @returns The instance's {@linkcode ReadableStream}.
   */ get readable() {
    return this.#readable;
  }
  /**
   * The instance's {@linkcode WritableStream}.
   *
   * @example Usage
   * ```ts no-assert
   * import { CsvParseStream } from "@std/csv/csv-parse-stream";
   *
   * const source = ReadableStream.from([
   *   "name,age",
   *   "Alice,34",
   *   "Bob,24",
   *   "Charlie,45",
   * ]);
   * const parseStream = new CsvParseStream();
   * const parts = source.pipeTo(parseStream.writable);
   * for await (const part of parseStream.readable) {
   *   console.log(part);
   * }
   * ```
   *
   * @returns The instance's {@linkcode WritableStream}.
   */ get writable() {
    return this.#lines.writable;
  }
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 12432 characters]
// denoCacheMetadata=4198932400963834694,269221701301482368