void VerifyGrid(string str, HaystackDictionary meta, object[] cols, HaystackValue[][] rows) { // normalize nulls if (meta == null) { meta = new HaystackDictionary(); } for (int i = 0; i < cols.Length; ++i) { if (cols[i] == null) { cols[i] = new HaystackDictionary(); } } // read from zinc var grid = new ZincReader(str).ReadValue <HaystackGrid>(); VerifyGridEquals(grid, meta, cols, rows); // write grid and verify we can parse that too string writeStr = ZincWriter.ToZinc(grid); var writeGrid = new ZincReader(writeStr).ReadValue <HaystackGrid>(); VerifyGridEquals(writeGrid, meta, cols, rows); }
/// <summary> /// Convenience method to call HisWriteNoWarnAsync with a "noWarn" marker to /// prevent warnings when writing out-of-order data. /// <param name="id">Record ID.</param> /// <param name="items">Time-series data.</param> public static Task <HaystackGrid> HisWriteNoWarnAsync(this IHaystackClient client, HaystackReference id, HaystackHistoryItem[] items) { var meta = new HaystackDictionary(); meta.Add("noWarn", new HaystackMarker()); return(client.HisWriteAsync(id, items, meta)); }
public void WriteEntity_QuotedString_IsValid() { using (var writer = new StringWriter()) { // Arrange. var trioWriter = new TrioWriter(writer); var entity = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["strTag"] = new HaystackString("Line with \"inline\" quotes."), }); // Act. trioWriter.WriteEntity(entity); var trio = writer.ToString(); // Assert. var target = @"dis:Site 1 site area:3702ft² geoAddr:""100 Main St, Richmond, VA"" geoCoord:C(37.5458,-77.4491) strTag:""Line with \""inline\"" quotes."" "; Assert.AreEqual(target.Replace("\r", ""), trio.Replace("\r", "")); } }
public void WriteEntity_MultiLineQuotedString_IsValid() { using (var writer = new StringWriter()) { // Arrange. var trioWriter = new TrioWriter(writer); var entity = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["summary"] = new HaystackString("This is a string value with \"quotes\" or (unsafe) characters which spans multiple\nlines with two or more space characters"), }); // Act. trioWriter.WriteEntity(entity); var trio = writer.ToString(); // Assert. var target = @"dis:Site 1 site area:3702ft² geoAddr:""100 Main St, Richmond, VA"" geoCoord:C(37.5458,-77.4491) summary: This is a string value with ""quotes"" or (unsafe) characters which spans multiple lines with two or more space characters "; Assert.AreEqual(target.Replace("\r", ""), trio.Replace("\r", "")); } }
private ZincWriter WriteDictionary(HaystackDictionary dict, bool addBraces = false) { if (addBraces) { WriteValue('{'); } bool bFirst = true; foreach (var kv in dict) { var name = kv.Key; var value = kv.Value; if (name != null) { if (!bFirst) { WriteValue(' '); } WriteValue(name); if (value is not HaystackMarker) { WriteValue(':').WriteValue(value); } bFirst = false; } } if (addBraces) { WriteValue('}'); } return(this); }
/// <summary> /// Read a dictionary of tags. /// </summary> public HaystackDictionary ReadDictionary() { var dictionary = new HaystackDictionary(); bool braces = _currentToken == HaystackToken.lbrace; if (braces) { Consume(HaystackToken.lbrace); } while (_currentToken == HaystackToken.id) { // tag name string id = ConsumeTagName(); if (!char.IsLower(id[0])) { throw GetException("Invalid dict tag name: " + id); } // tag value HaystackValue val = HaystackMarker.Instance; if (_currentToken == HaystackToken.colon) { Consume(HaystackToken.colon); val = ParseValue(); } dictionary.Add(id, val); } if (braces) { Consume(HaystackToken.rbrace); } return(dictionary); }
public void WriteEntity_DateTime_CorrectKind() { using (var writer = new StringWriter()) using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) { // Arrange. var haysonWriter = new HaysonWriter(jsonWriter); var entity = new HaystackDictionary(); entity.Add("ts", new HaystackDateTime(new DateTime(2020, 1, 1), HaystackTimeZone.UTC)); // Act. haysonWriter.WriteEntity(entity); var hayson = writer.ToString(); // Assert. var target = @"{ ""ts"": { ""_kind"": ""dateTime"", ""val"": ""2020-01-01T00:00:00.0000000+00:00"", ""tz"": ""UTC"" } }"; Assert.AreEqual(target.Replace("\r", ""), hayson.Replace("\r", "")); } }
public void TestBasics() { var tags = new HaystackDictionary(); tags.Add("id", new HaystackReference("aaaa-bbbb")); tags.Add("site", new HaystackMarker()); tags.Add("geoAddr", new HaystackString("Richmond, Va")); tags.Add("area", new HaystackNumber(1200, "ft")); tags.Add("date", new HaystackDate(2000, 12, 3)); tags.Add("null", null); // size Assert.AreEqual(5, tags.Count); Assert.IsFalse(tags.IsEmpty()); // configured tags Assert.IsTrue(tags.Get("id").Equals(new HaystackReference("aaaa-bbbb"))); Assert.IsTrue(tags.Get("site").Equals(new HaystackMarker())); Assert.IsTrue(tags.Get("geoAddr").Equals(new HaystackString("Richmond, Va"))); Assert.IsTrue(tags.Get("area").Equals(new HaystackNumber(1200, "ft"))); Assert.IsTrue(tags.Get("date").Equals(new HaystackDate(2000, 12, 3))); Assert.ThrowsException <HaystackUnknownNameException>(() => tags.Get("null")); // missing tag Assert.IsFalse(tags.ContainsKey("foo")); Assert.ThrowsException <HaystackUnknownNameException>(() => tags.Get("foo")); }
public void WriteEntity_UnsafeString_IsValid() { using (var writer = new StringWriter()) { // Arrange. var trioWriter = new TrioWriter(writer); var entity = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["strTag"] = new HaystackString("Not ok if unquoted (with unsafe chars)."), ["guid"] = new HaystackString("cc49e18d-bef0-446f-a106-17eeb7c2eb53"), }); // Act. trioWriter.WriteEntity(entity); var trio = writer.ToString(); // Assert. var target = @"dis:Site 1 site area:3702ft² geoAddr:""100 Main St, Richmond, VA"" geoCoord:C(37.5458,-77.4491) strTag:""Not ok if unquoted (with unsafe chars)."" guid:""cc49e18d-bef0-446f-a106-17eeb7c2eb53"" "; Assert.AreEqual(target.Replace("\r", ""), trio.Replace("\r", "")); } }
void VerifyGridEquals(HaystackGrid grid, HaystackDictionary meta, object[] cols, HaystackValue[][] rows) { // meta Assert.IsTrue(grid.Meta.Equals(meta)); // cols Assert.AreEqual(grid.ColumnCount, cols.Length / 2); for (int i = 0; i < grid.ColumnCount; ++i) { Assert.AreEqual(grid.Column(i).Name, cols[i * 2 + 0]); Assert.IsTrue(grid.Column(i).Meta.Equals(cols[i * 2 + 1])); } // rows Assert.AreEqual(grid.RowCount, rows.Length); for (int ri = 0; ri < rows.Length; ++ri) { var expected = rows[ri]; var actual = grid.Row(ri); for (int ci = 0; ci < expected.Length; ++ci) { Assert.AreEqual(expected[ci], actual[grid.Column(ci).Name]); } } }
public void WriteEntity_NestedGrid_IsValid() { using (var writer = new StringWriter()) using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) { // Arrange. var haysonWriter = new HaysonWriter(jsonWriter); var grid = new HaystackGrid() .AddColumn("b", col => col.Meta.AddString("dis", "Column B")) .AddColumn("a") .AddRow(new HaystackNumber(20), new HaystackNumber(10)); var entity = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["type"] = new HaystackString("grid"), ["val"] = grid, }); // Act. haysonWriter.WriteEntity(entity); var hayson = writer.ToString(); // Assert. var target = @"{ ""type"": ""grid"", ""val"": { ""_kind"": ""grid"", ""cols"": [ { ""name"": ""b"", ""meta"": { ""dis"": ""Column B"" } }, { ""name"": ""a"" } ], ""rows"": [ { ""b"": { ""_kind"": ""number"", ""val"": 20.0, ""unit"": null }, ""a"": { ""_kind"": ""number"", ""val"": 10.0, ""unit"": null } } ] } }"; Assert.AreEqual(target.Replace("\r", ""), hayson.Replace("\r", "")); } }
public void TestRemove() { HaystackDictionary dict = new HaystackDictionary().AddMarker("x").AddString("y", "str"); dict.Remove("y"); Assert.AreEqual(1, dict.Count); Assert.AreEqual(new HaystackMarker(), dict["x"]); }
public static TValue GetUnchecked <TValue>(this HaystackDictionary dict, string name) where TValue : HaystackValue { if (dict.ContainsKey(name)) { return(dict.Get <TValue>(name)); } return(null); }
private ZincWriter WriteMeta(HaystackDictionary meta) { if (meta.IsEmpty()) { return(this); } WriteValue(' '); return(WriteDictionary(meta)); }
/// <summary> /// Invoke an action using the "invokeAction" call. /// </summary> /// <param name="id">Target to invoke the action on.</param> /// <param name="action">Action to invoke.</param> /// <param name="args">Action arguments.</param> /// <returns>Action result grid.</returns> public Task <HaystackGrid> InvokeActionAsync(HaystackReference id, string action, HaystackDictionary args) { var meta = new HaystackDictionary(); meta.Add("id", id); meta.Add("action", new HaystackString(action)); var req = new HaystackGrid(new[] { args }, meta); return(CallAsync("invokeAction", req)); }
public void TestAdd() { HaystackDictionary dict = new HaystackDictionary().AddMarker("x").AddMarker("y"); dict.Add("z", new HaystackString("z")); Assert.AreEqual(3, dict.Count); Assert.AreEqual(new HaystackMarker(), dict["x"]); Assert.AreEqual(new HaystackMarker(), dict["y"]); Assert.AreEqual(new HaystackString("z"), dict["z"]); }
public void WriteEntities_NestedGrid_IsValid() { using (var writer = new StringWriter()) { // Arrange. var trioWriter = new TrioWriter(writer); var entity1 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["type"] = new HaystackString("list"), ["val"] = new HaystackList(new HaystackNumber(1), new HaystackNumber(2), new HaystackNumber(3)), }); var entity2 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["type"] = new HaystackString("dict"), ["val"] = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Dict!"), ["foo"] = new HaystackMarker(), }), }); var grid = new HaystackGrid() .AddColumn("b") .AddColumn("a") .AddRow(new HaystackNumber(20), new HaystackNumber(10)); var entity3 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["type"] = new HaystackString("grid"), ["val"] = grid, }); // Act. trioWriter.WriteComment("Trio"); trioWriter.WriteEntity(entity1); trioWriter.WriteEntity(entity2); trioWriter.WriteEntity(entity3); var trio = writer.ToString(); // Assert. var target = @"// Trio type:list val:[1,2,3] --- type:dict val:{dis:""Dict!"" foo} --- type:grid val:Zinc: ver:""3.0"" b,a 20,10 "; Assert.AreEqual(target.Replace("\r", ""), trio.Replace("\r", "")); } }
public void TestToArray() { HaystackDictionary a = new HaystackDictionary().AddMarker("x").AddString("y", "str"); var array = a.ToArray(); Assert.AreEqual(2, array.Length); Assert.AreEqual("x", array[0].Key); Assert.AreEqual(new HaystackMarker(), array[0].Value); Assert.AreEqual("y", array[1].Key); Assert.AreEqual(new HaystackString("str"), array[1].Value); }
public void TestEmpty() { // Instance Empty var tags = new HaystackDictionary(); Assert.AreEqual(tags, new HaystackDictionary()); // size Assert.AreEqual(tags.Count, 0); Assert.IsTrue(tags.IsEmpty()); // missing tag Assert.IsFalse(tags.ContainsKey("foo")); }
public void TestNullGridMetaAndColMeta() { var meta = new HaystackDictionary(); meta.AddMarker("tag"); var col0Meta = new HaystackDictionary(); var col1Meta = new HaystackDictionary(); col1Meta.AddMarker("markermetatag"); VerifyGrid("ver:\"3.0\" tag:M\na nullmetatag:N, b markermetatag\n", meta, new object[] { "a", col0Meta, "b", col1Meta }, new HaystackValue[0][] ); }
public void WriteEntity(HaystackDictionary entity) { _haysonWriter.WriteStartObject(); foreach (var kv in entity) { if (kv.Value == null) { continue; } _haysonWriter.WritePropertyName(kv.Key); WriteValue(kv.Value); } _haysonWriter.WriteEndObject(); }
/// <summary> /// Read a single record by its unique ID. /// Throws an exception if the record was not found. /// </summary> /// <param name="id">Record ID.</param> /// <returns>Matching record.</returns> public async Task <HaystackDictionary> ReadByIdAsync(HaystackReference id) { HaystackGrid res = await ReadByIdsAsync(new HaystackReference[] { id }); if (res.IsEmpty()) { throw new Exception($"Record not found for: {id}"); } HaystackDictionary rec = res.Row(0); if (!rec.ContainsKey("id")) { throw new Exception($"Record not found for: {id}"); } return(rec); }
public void WriteEntity_QuotedString_IsValid() { using (var writer = new StringWriter()) using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) { // Arrange. var haysonWriter = new HaysonWriter(jsonWriter); var entity = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["strTag"] = new HaystackString("Line with \"inline\" quotes."), }); // Act. haysonWriter.WriteEntity(entity); var hayson = writer.ToString(); // Assert. var target = @"{ ""dis"": ""Site 1"", ""site"": { ""_kind"": ""marker"" }, ""area"": { ""_kind"": ""number"", ""val"": 3702.0, ""unit"": ""ft²"" }, ""geoAddr"": ""100 Main St, Richmond, VA"", ""geoCoord"": { ""_kind"": ""coord"", ""lat"": 37.5458, ""lng"": -77.4491 }, ""strTag"": ""Line with \""inline\"" quotes."" }"; Assert.AreEqual(target.Replace("\r", ""), hayson.Replace("\r", "")); } }
public void WriteEntities_TwoEntities_IsValid() { using (var writer = new StringWriter()) { // Arrange. var trioWriter = new TrioWriter(writer); var entity1 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["strTag"] = new HaystackString("OK if unquoted if only safe chars"), ["summary"] = new HaystackString("This is a string value which spans multiple\nlines with two or more space characters"), }); var entity2 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["name"] = new HaystackString("Site 2"), ["site"] = new HaystackMarker(), ["summary"] = new HaystackString("Entities are separated by one or more dashes"), }); // Act. trioWriter.WriteEntity(entity1); trioWriter.WriteEntity(entity2); var trio = writer.ToString(); // Assert. var target = @"dis:Site 1 site area:3702ft² geoAddr:""100 Main St, Richmond, VA"" geoCoord:C(37.5458,-77.4491) strTag:OK if unquoted if only safe chars summary: This is a string value which spans multiple lines with two or more space characters --- name:Site 2 site summary:Entities are separated by one or more dashes "; Assert.AreEqual(target.Replace("\r", ""), trio.Replace("\r", "")); } }
public void WriteEntity(HaystackDictionary entity) { if (isFirst) { isFirst = false; } else { _trioWriter.WriteLine("---"); } foreach (var kv in entity) { if (kv.Value == null) { continue; } _trioWriter.Write(kv.Key); if (kv.Value is HaystackMarker) { _trioWriter.WriteLine(); continue; } _trioWriter.Write(":"); if (kv.Value is HaystackGrid) { _trioWriter.Write("Zinc:"); _trioWriter.WriteLine(); var val = ZincWriter.ToZinc(kv.Value); foreach (var line in val.TrimEnd().Split(new[] { "\n" }, StringSplitOptions.None)) { _trioWriter.Write(" "); _trioWriter.WriteLine(line.TrimEnd()); } } else if (kv.Value is HaystackString stringValue && stringValue.Value.Contains("\n")) { var val = stringValue.Value; _trioWriter.WriteLine(); foreach (var line in val.TrimEnd().Split(new[] { "\n" }, StringSplitOptions.None)) { _trioWriter.Write(" "); _trioWriter.WriteLine(line.TrimEnd()); } }
public void TestEquality() { var a = new HaystackDictionary(); a.Add("x", new HaystackMarker()); Assert.IsTrue(a.Equals(new HaystackDictionary().AddMarker("x"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddNumber("x", 3))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddMarker("y"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddMarker("x").AddMarker("y"))); a = new HaystackDictionary().AddMarker("x").AddString("y", "str"); Assert.IsTrue(a.Equals(new HaystackDictionary().AddMarker("x").AddString("y", "str"))); Assert.IsTrue(a.Equals(new HaystackDictionary().AddString("y", "str").AddMarker("x"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddString("x", "str").AddString("y", "str"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddMarker("x").AddString("y", "strx"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddString("y", "str"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddMarker("x"))); Assert.IsFalse(a.Equals(new HaystackDictionary().AddMarker("x").AddString("yy", "str"))); a = new HaystackDictionary().AddValue("x", null); Assert.IsTrue(a.Equals(new HaystackDictionary().AddValue("x", null))); Assert.IsTrue(a.Equals(new HaystackDictionary().AddValue("foo", null).AddValue("bar", null))); Assert.IsTrue(a.Equals(new HaystackDictionary())); }
public HaystackColumn(int index, string name, HaystackDictionary meta = null) { Index = index; Name = name; Meta = meta ?? new HaystackDictionary(); }
public void WriteEntities_TwoEntities_IsValid() { using (var writer = new StringWriter()) using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) { // Arrange. var haysonWriter = new HaysonWriter(jsonWriter); var entity1 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["dis"] = new HaystackString("Site 1"), ["site"] = new HaystackMarker(), ["area"] = new HaystackNumber(3702, "ft²"), ["geoAddr"] = new HaystackString("100 Main St, Richmond, VA"), ["geoCoord"] = new HaystackCoordinate(37.5458m, -77.4491m), ["strTag"] = new HaystackString("Line with \"inline\" quotes."), ["summary"] = new HaystackString("This is a string value which spans multiple\nlines with two or more space characters"), }); var entity2 = new HaystackDictionary(new Dictionary <string, HaystackValue> { ["name"] = new HaystackString("Site 2"), ["site"] = new HaystackMarker(), ["summary"] = new HaystackString("Entities are separated by one or more dashes"), }); // Act. haysonWriter.WriteEntities(entity1, entity2); var hayson = writer.ToString(); // Assert. var target = @"[ { ""dis"": ""Site 1"", ""site"": { ""_kind"": ""marker"" }, ""area"": { ""_kind"": ""number"", ""val"": 3702.0, ""unit"": ""ft²"" }, ""geoAddr"": ""100 Main St, Richmond, VA"", ""geoCoord"": { ""_kind"": ""coord"", ""lat"": 37.5458, ""lng"": -77.4491 }, ""strTag"": ""Line with \""inline\"" quotes."", ""summary"": ""This is a string value which spans multiple\nlines with two or more space characters"" }, { ""name"": ""Site 2"", ""site"": { ""_kind"": ""marker"" }, ""summary"": ""Entities are separated by one or more dashes"" } ]"; Assert.AreEqual(target.Replace("\r", ""), hayson.Replace("\r", "")); } }
public void TestCheckedImplicitMissing() { var tags = new HaystackDictionary(); tags.Get("foo"); }
private HaystackGrid ReadGrid() { bool nested = _currentToken == HaystackToken.lt2; if (nested) { Consume(HaystackToken.lt2); if (_currentToken == HaystackToken.nl) { Consume(HaystackToken.nl); } } bool bValisVer = false; if (_currentValue is string) { if (((string)_currentValue).CompareTo("ver") == 0) { bValisVer = true; } } // ver:"3.0" if (_currentToken != HaystackToken.id || !bValisVer) { throw GetException("Expecting grid 'ver' identifier, not " + curToStr()); } Consume(); Consume(HaystackToken.colon); _version = CheckVersion(ConsumeStr()); // grid meta var grid = new HaystackGrid(); if (_currentToken == HaystackToken.id) { var dict = ReadDictionary(); foreach (var kv in dict) { grid.AddMeta(kv.Key, kv.Value); } } Consume(HaystackToken.nl); // column definitions int numCols = 0; while (_currentToken == HaystackToken.id) { ++numCols; string name = ConsumeTagName(); var colMeta = new HaystackDictionary(); if (_currentToken == HaystackToken.id) { colMeta = ReadDictionary(); } grid.AddColumn(name, colMeta); if (_currentToken != HaystackToken.comma) { break; } Consume(HaystackToken.comma); } if (numCols == 0) { throw GetException("No columns defined"); } Consume(HaystackToken.nl); // grid rows while (true) { if (_currentToken == HaystackToken.nl) { break; } if (_currentToken == HaystackToken.eof) { break; } if (nested && _currentToken == HaystackToken.gt2) { break; } // read cells HaystackValue[] cells = new HaystackValue[numCols]; for (int i = 0; i < numCols; ++i) { if (_currentToken == HaystackToken.comma || _currentToken == HaystackToken.nl || _currentToken == HaystackToken.eof) { cells[i] = null; } else { cells[i] = ParseValue(); } if (i + 1 < numCols) { Consume(HaystackToken.comma); } } grid.AddRow(cells); // newline or end if (nested && _currentToken == HaystackToken.gt2) { break; } if (_currentToken == HaystackToken.eof) { break; } Consume(HaystackToken.nl); } if (_currentToken == HaystackToken.nl) { Consume(HaystackToken.nl); } if (nested) { Consume(HaystackToken.gt2); } return(grid); }