Skip to content

moonbit-community/csv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MoonBit CSV Library

A comprehensive CSV (Comma-Separated Values) parsing and stringification library for MoonBit, migrated from the Deno standard library with full RFC 4180 compliance.

Features

Parsing Features

  • âś… RFC 4180 Compliant: Full support for CSV specification including proper quote handling
  • âś… Quoted Fields: Handle fields containing separators, quotes, and newlines
  • âś… Quote Escaping: Proper handling of escaped quotes ("" becomes ")
  • âś… Multiple Line Endings: Support for LF (\n), CRLF (\r\n), and CR (\r)
  • âś… BOM Detection: Automatic handling of Byte Order Mark (\ufeff)
  • âś… Comment Lines: Skip lines starting with a comment character
  • âś… Custom Separators: Support any separator (comma, semicolon, tab, etc.)
  • âś… Flexible Field Validation: Optional field count validation per row
  • âś… Lazy Quotes: Option to allow unescaped quotes in fields
  • âś… Leading Space Trimming: Optional trimming of leading whitespace
  • âś… Object Parsing: Convert CSV to array of objects using headers

Stringification Features

  • âś… Automatic Quoting: Fields containing separators, quotes, or newlines are automatically quoted
  • âś… Quote Escaping: Proper escaping of quotes in output
  • âś… CRLF Line Endings: Uses \r\n as per RFC 4180
  • âś… BOM Support: Optional Byte Order Mark for Unicode compatibility
  • âś… Custom Separators: Support any separator for output
  • âś… Object Support: Convert arrays of objects to CSV with column control
  • âś… Header Control: Optional headers in output

Quick Start

Basic Parsing

test "basic parsing example" {
  let csv_data = "name,age,city\njohn,30,nyc\nmary,25,la"
  let rows = @csv.parse(csv_data)
  
  inspect(rows.length(), content="3")
  inspect(rows[0], content=["name", "age", "city"])
  inspect(rows[1], content=["john", "30", "nyc"])
}

Parsing with Options

test "parsing with options" {
  let csv_data = "# Comment line\nname;age;city\njohn;30;nyc"
  let options = @csv.ParseOptions::{
    separator: ";",
    comment: Some("#"),
    skip_first_row: false,
  }
  let rows = @csv.parse_with_options(csv_data, options)
  
  inspect(rows.length(), content="2") // Comment line skipped
}

Advanced Parsing Features

test "advanced parsing" {
  let csv_data = " \"john, jr\" , 30 , \"new\nyork\" "
  let options = @csv.AdvancedParseOptions::{
    separator: ",",
    comment: None,
    trim_leading_space: true,
    lazy_quotes: false,
    fields_per_record: -1,
    skip_first_row: false,
    columns: None,
  }
  let rows = @csv.parse_with_advanced_options(csv_data, options)
  
  // Properly handles quoted fields with separators and newlines
  inspect(rows[0], content=["john, jr", "30", "new\nyork"])
}

Object Parsing

test "object parsing" {
  let csv_data = "name,age,city\njohn,30,nyc\nmary,25,la"
  let options = @csv.ParseOptions::{
    separator: ",",
    comment: None,
    skip_first_row: true, // Use first row as headers
  }
  let objects = @csv.parse_to_objects(csv_data, Some(options))
  
  inspect(objects.length(), content="2")
  inspect(objects[0].get("name"), content=Some("john"))
  inspect(objects[0].get("age"), content=Some("30"))
}

Basic Stringification

test "basic stringification" {
  let data = [
    ["name", "age", "city"],
    ["john", "30", "nyc"],
    ["mary", "25", "la"],
  ]
  let csv_output = @csv.stringify(data)
  
  inspect(csv_output, content="name,age,city\r\njohn,30,nyc\r\nmary,25,la\r\n")
}

Object Stringification

test "object stringification" {
  let data = [
    @Map.from_array([("name", "john"), ("age", "30"), ("city", "nyc")]),
    @Map.from_array([("name", "mary"), ("age", "25"), ("city", "la")]),
  ]
  let columns = ["name", "age", "city"]
  let csv_output = @csv.stringify_with_columns(data, columns)
  
  inspect(csv_output, content="name,age,city\r\njohn,30,nyc\r\nmary,25,la\r\n")
}

Handling Special Characters

test "special characters" {
  let data = [
    ["name", "description"],
    ["john", "a \"nice\" person"],
    ["mary", "lives in, NYC"],
    ["bob", "address:\n123 Main St"],
  ]
  let csv_output = @csv.stringify(data)
  
  // Automatically quotes fields with special characters
  // Properly escapes quotes as ""
}

API Reference

Parse Functions

parse(input: String) -> Array[Array[String]]

Parse CSV string with default options.

parse_with_options(input: String, options: ParseOptions) -> Array[Array[String]]

Parse CSV string with basic options.

parse_with_advanced_options(input: String, options: AdvancedParseOptions) -> Array[Array[String]]

Parse CSV string with all advanced features.

parse_to_objects(input: String, options: ParseOptions?) -> Array[Map[String, String]]

Parse CSV string to array of objects using headers.

Stringify Functions

stringify(data: Array[Array[String]]) -> String

Convert array of arrays to CSV string with default options.

stringify_arrays(data: Array[Array[String]], options: StringifyOptions?) -> String

Convert array of arrays to CSV string with options.

stringify_objects(data: Array[Map[String, String]], columns: Array[String]?, options: StringifyOptions?) -> String

Convert array of objects to CSV string.

stringify_with_columns(data: Array[Map[String, String]], columns: Array[String]) -> String

Convert array of objects to CSV string with specified columns.

Options Types

ParseOptions

Basic parsing options for backward compatibility:

  • separator: String - Field separator (default: ",")
  • comment: String? - Comment line prefix (default: None)
  • skip_first_row: Bool - Skip first row for headers (default: false)

AdvancedParseOptions

Advanced parsing options with all features:

  • separator: String - Field separator (default: ",")
  • comment: String? - Comment line prefix (default: None)
  • trim_leading_space: Bool - Trim leading whitespace (default: false)
  • lazy_quotes: Bool - Allow unescaped quotes (default: false)
  • fields_per_record: Int - Field count validation (default: -1 for any)
  • skip_first_row: Bool - Skip first row for headers (default: false)
  • columns: Array[String]? - Header column names (default: None)

StringifyOptions

Stringification options:

  • separator: String - Field separator (default: ",")
  • headers: Bool - Include headers (default: true)
  • bom: Bool - Include BOM (default: false)

RFC 4180 Compliance

This library fully implements RFC 4180 specification:

  1. CRLF Line Endings: Uses \r\n for line breaks
  2. Quote Escaping: Doubles quotes ("") to escape them
  3. Field Quoting: Automatically quotes fields containing separators, quotes, or newlines
  4. Header Support: Optional header row handling
  5. Empty Fields: Proper handling of empty fields
  6. Unicode Support: BOM detection and proper Unicode handling

Error Handling

The library provides detailed error messages with line and column information for parsing errors:

  • Syntax Errors: Reports exact line and column of parsing issues
  • Quote Errors: Specific messages for unescaped or malformed quotes
  • Field Count Errors: Clear messages when field count validation fails
  • Invalid Delimiters: Prevents use of invalid separator or comment characters

Migration from Deno std

This library is migrated from the Deno standard library's CSV module, ensuring:

  • Feature parity with the TypeScript implementation
  • RFC 4180 compliance
  • Comprehensive test coverage (45+ test cases)
  • Performance optimization for MoonBit
  • Proper error handling with MoonBit's type system

Testing

The library includes comprehensive test coverage:

  • Basic parsing and stringification
  • RFC 4180 compliance tests
  • Edge cases (BOM, empty fields, quotes)
  • Advanced features (lazy quotes, trimming, validation)
  • Integration tests (round-trip parsing/stringification)
  • Error condition handling

Run tests with:

moon test

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published