import { parseLocString } from '@jbrowse/core/util' ; export function bufferToString ( buffer ) { return new TextDecoder ( 'utf-8' , { fatal : true } ) . decode ( buffer ) ; } async function parseWith ( buffer , options = { } ) { const csv = await import ( 'csvtojson' ) . then ( module => module . default ) ; return csv ( { noheader : true , output : 'csv' , ... options } ) . fromString ( bufferToString ( buffer ) ) ; } export interface Row { id : string ; extendedData ? : any ; cells : { text : string ; extendedData ? : any ; } [ ] ; } export interface RowSet { isLoaded : boolean ; rows : Row [ ] ; } export interface ParseOptions { hasColumnNameLine ? : boolean ; columnNameLineNumber ? : number ; selectedAssemblyName ? : string ; isValidRefName : ( refName : string , assemblyName ? : string ) => boolean ; } export interface Column { name : string ; dataType : { type : string ; } ; isDerived ? : boolean ; derivationFunctionText ? : string ; } function guessColumnType ( rowSet , columnNumber , isValidRefName ) { const text = rowSet . rows [ 0 ] . cells [ columnNumber ] . text || '' ; let guessedType = 'Text' ; let parsedLoc ; try { parsedLoc = parseLocString ( text , isValidRefName ) ; } catch ( error ) { } if ( parsedLoc && parsedLoc . refName && typeof parsedLoc . start === 'number' ) { guessedType = 'LocString' ; } else if ( /^\d+(\.\d+)?$/ . test ( text ) ) { guessedType = 'Number' ; } return guessedType ; } function dataToSpreadsheetSnapshot ( rows , options = { hasColumnNameLine : false , columnNameLineNumber : 1 , isValidRefName : ( ) => false , } ) { let maxCols = 0 ; const rowSet = { isLoaded : true , rows : rows . map ( ( row , rowNumber ) => { const id = rowNumber + ( options . hasColumnNameLine ? 0 : 1 ) ; if ( row . length > maxCols ) { maxCols = row . length ; } return { id : String ( id ) , cells : row . map ( ( text , columnNumber ) => { return { columnNumber , text } ; } ) , } ; } ) , } ; const columnNames = { } ; if ( options . hasColumnNameLine && options . columnNameLineNumber !== undefined ) { const [ colNamesRow ] = rowSet . rows . splice ( options . columnNameLineNumber - 1 , 1 ) ; if ( colNamesRow ) { colNamesRow . cells . forEach ( ( cell , columnNumber ) => { columnNames [ columnNumber ] = cell . text || '' ; } ) ; } } const columns = [ ] ; const columnDisplayOrder = [ ] ; for ( let columnNumber = 0 ; columnNumber < maxCols ; columnNumber += 1 ) { columnDisplayOrder . push ( columnNumber ) ; const guessedType = guessColumnType ( rowSet , columnNumber , options . isValidRefName ) ; if ( guessedType === 'LocString' ) { rowSet . rows . forEach ( row => { const cell = row . cells [ columnNumber ] ; cell . extendedData = parseLocString ( cell . text , options . isValidRefName ) ; } ) ; } columns [ columnNumber ] = { name : columnNames [ columnNumber ] , dataType : { type : guessedType , } , } ; } return { rowSet , columnDisplayOrder , hasColumnNames : ! ! options . hasColumnNameLine , columns , assemblyName : options . selectedAssemblyName , } ; } export async function parseCsvBuffer ( buffer , options = { hasColumnNameLine : false , columnNameLineNumber : 1 , isValidRefName : ( ) => false , } ) { const rows = await parseWith ( buffer ) ; return dataToSpreadsheetSnapshot ( rows , options ) ; } export async function parseTsvBuffer ( buffer , options = { hasColumnNameLine : false , columnNameLineNumber : 1 , isValidRefName : ( ) => false , } ) { const rows = await parseWith ( buffer , { delimiter : '\t' } ) ; return dataToSpreadsheetSnapshot ( rows , options ) ; }