/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using CsvHelper;
using System.Collections.Generic;
using System.Linq;
#nullable enable
namespace Yarn.Unity
{
///
/// Struct holding information about a line and its associated metadata.
/// Only used internally as an intermediary before persisting information
/// in either a `YarnProject` or a CSV file.
///
internal struct LineMetadataTableEntry
{
///
/// The line ID for this line.
///
public string ID;
///
/// The name of the Yarn script in which this line was originally
/// found.
///
public string File;
///
/// The name of the node in which this line was originally found.
///
///
/// This node can be found in the file indicated by .
///
public string Node;
///
/// The line number in the file indicated by at
/// which the original version of this line can be found.
///
public string LineNumber;
///
/// Additional metadata included in this line.
///
public string[] Metadata;
///
/// Initializes a new instance of the
/// struct, copying values from an existing instance.
///
/// The instance to copy values from.
public LineMetadataTableEntry(LineMetadataTableEntry s)
{
ID = s.ID;
File = s.File;
Node = s.Node;
LineNumber = s.LineNumber;
Metadata = s.Metadata;
}
private static CsvHelper.Configuration.Configuration? CsvConfiguration;
private static CsvHelper.Configuration.Configuration GetConfiguration()
{
if (CsvConfiguration == null)
{
CsvConfiguration = new CsvHelper.Configuration.Configuration(System.Globalization.CultureInfo.InvariantCulture)
{
MemberTypes = CsvHelper.Configuration.MemberTypes.Fields,
};
}
return CsvConfiguration;
}
///
/// Reads comma-separated value data from ,
/// and produces a collection of structs.
///
/// A string containing CSV-formatted
/// data.
/// The parsed collection of
/// structs.
/// Thrown when an error occurs when
/// parsing the string.
internal static IEnumerable ParseFromCSV(string sourceText)
{
try
{
using (var stringReader = new System.IO.StringReader(sourceText))
using (var csv = new CsvReader(stringReader, GetConfiguration()))
{
/*
Do the below instead of GetRecords due to
incompatibility with IL2CPP See more:
https://github.com/YarnSpinnerTool/YarnSpinner-Unity/issues/36#issuecomment-691489913
*/
var records = new List();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
// Fetch values; if they can't be found, they'll be
// defaults.
csv.TryGetField("id", out var id);
csv.TryGetField("file", out var file);
csv.TryGetField("node", out var node);
csv.TryGetField("lineNumber", out var lineNumber);
csv.TryGetField("metadata", out var metadata);
var record = new LineMetadataTableEntry
{
ID = id ?? string.Empty,
File = file ?? string.Empty,
Node = node ?? string.Empty,
LineNumber = lineNumber ?? string.Empty,
Metadata = metadata?.Split(' ') ?? new string[] { },
};
records.Add(record);
}
return records;
}
}
catch (CsvHelperException e)
{
throw new System.ArgumentException($"Error reading CSV file: {e}");
}
}
///
/// Creates a CSV-formatted string containing data from .
///
/// The values to
/// generate the spreadsheet from.
/// A string containing CSV-formatted data.
public static string CreateCSV(IEnumerable entries)
{
using (var textWriter = new System.IO.StringWriter())
{
// Use the invariant culture when writing the CSV
var csv = new CsvWriter(
textWriter, // write into this stream
GetConfiguration() // use this configuration
);
var fieldNames = new[] {
"id",
"file",
"node",
"lineNumber",
"metadata",
};
foreach (var field in fieldNames)
{
csv.WriteField(field);
}
csv.NextRecord();
foreach (var entry in entries)
{
var values = new[] {
entry.ID,
entry.File,
entry.Node,
entry.LineNumber,
string.Join(" ", entry.Metadata),
};
foreach (var value in values)
{
csv.WriteField(value);
}
csv.NextRecord();
}
return textWriter.ToString();
}
}
///
public override string ToString()
{
return $"LineMetadataTableEntry: id={ID} file={File} node={Node} line={LineNumber} metadata={string.Join(" ", Metadata)}";
}
///
public override bool Equals(object obj)
{
return obj is LineMetadataTableEntry entry &&
ID == entry.ID &&
File == entry.File &&
Node == entry.Node &&
LineNumber == entry.LineNumber &&
Enumerable.SequenceEqual(Metadata, entry.Metadata);
}
///
public override int GetHashCode()
{
var result =
ID.GetHashCode() ^
File.GetHashCode() ^
Node.GetHashCode() ^
LineNumber.GetHashCode();
foreach (var piece in Metadata)
{
result ^= piece.GetHashCode();
}
return result;
}
}
}