/// <summary> /// Emits a sequence end. /// </summary> /// <param name="event">The event.</param> private void EmitSequenceEnd(HclEvent @event) { GetTypedEvent <SequenceEnd>(@event); this.state = this.PopState(); if (this.previousState == EmitterState.BlockList) { // End of block list while (this.state == EmitterState.BlockList) { this.state = this.PopState(); } return; } this.indent = this.indents.Pop(); this.WriteIndent(); this.WriteIndicator("]", false, false, false); if (this.state == EmitterState.Sequence) { this.Write(','); } }
/// <summary> /// Emits the next event. /// Events are queued until an entire resource is collected, the that resource is written out. /// </summary> /// <param name="event">The event.</param> public void Emit(HclEvent @event) { this.events.Enqueue(@event); if (@event.Type != EventType.ResourceEnd) { return; } try { // Eliminate attributes we don't want to serialize, // thus everything remaining in the queue following // this operation will be emitted. new EventQueuePreprocessor(this.events).ProcessQueue(); this.state = EmitterState.Resource; while (this.events.Any()) { this.EmitNode(this.events.Dequeue()); } } catch (HclSerializerException) { throw; } catch (Exception e) { throw new HclSerializerException( $"Internal error: {e.Message}", this.currentResourceName, this.currentResourceType, e); } }
/// <summary> /// Emits a mapping start. /// </summary> /// <param name="event">A <see cref="MappingStart"/> event.</param> private void EmitMappingStart(HclEvent @event) { GetTypedEvent <MappingStart>(@event); this.PushState(this.state); this.state = EmitterState.Mapping; this.WriteIndicator("{", true, false, false); this.IncreaseIndent(); this.WriteIndent(); }
/// <summary> /// Asserts an event is of the requested type and casts to it. /// </summary> /// <typeparam name="T">Expected subtype of the event passed as argument.</typeparam> /// <param name="event">The event.</param> /// <returns>Type casted event.</returns> /// <exception cref="System.ArgumentException">Expected {typeof(T).Name} - event</exception> private static T GetTypedEvent <T>(HclEvent @event) where T : HclEvent { if (!(@event is T hclEvent)) { throw new ArgumentException($"Expected {typeof(T).Name}", nameof(@event)); } return(hclEvent); }
/// <summary> /// Emits a mapping key. /// </summary> /// <param name="event">A <see cref="MappingKey"/> event.</param> private void EmitMappingKey(HclEvent @event) { var key = GetTypedEvent <MappingKey>(@event); if (EmitLifecycle.ContainsKey(this.currentResourceType) && EmitLifecycle[this.currentResourceType].Contains(key.Path)) { this.lifecycleKeys.Add(key.Path); } // Don't push path for repeating block key if (!key.IsBlockKey) { if (!this.isJson) { this.currentPath = key.Path; } this.WriteIndent(); } else { // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault switch (key.InitialAnalysis) { case AttributeContent.BlockList: this.blockKeys.Push(key); this.currentBlockKey = key; this.PushState(this.state); this.state = EmitterState.BlockList; break; case AttributeContent.BlockObject: this.state = EmitterState.BlockObject; break; default: this.state = EmitterState.Mapping; break; } this.WriteBreak(); this.WriteIndent(); } this.EmitScalar(@event); if (this.state == EmitterState.Mapping) { this.WriteIndicator("=", true, false, false); } }
/// <summary> /// Emits a scalar value. /// </summary> /// <param name="event">A <see cref="ScalarValue"/> event.</param> private void EmitScalarValue(HclEvent @event) { var scalar = GetTypedEvent <ScalarValue>(@event); if (this.state == EmitterState.Sequence) { this.WriteIndent(); } this.EmitScalar(this.resourceTraits.ApplyDefaultValue(this.currentPath, scalar)); }
/// <summary> /// Emits a policy start. /// </summary> /// <param name="event">The event.</param> private void EmitJsonStart(HclEvent @event) { GetTypedEvent <JsonStart>(@event); this.isJson = true; this.PushState(this.state); this.state = EmitterState.Json; this.WriteIndicator("jsonencode(", true, false, true); this.IncreaseIndent(); this.WriteIndent(); }
/// <summary> /// Emits the resource start. /// </summary> /// <param name="event">A <see cref="ResourceStart"/> event.</param> private void EmitResourceStart(HclEvent @event) { var rs = GetTypedEvent <ResourceStart>(@event); this.Write("resource"); this.isWhitespace = false; this.resourceTraits = AwsSchema.GetResourceTraits(rs.ResourceType); this.currentResourceName = rs.ResourceName; this.currentResourceType = rs.ResourceType; this.EmitScalar(new Scalar(rs.ResourceType, true)); this.EmitScalar(new Scalar(rs.ResourceName, true)); }
/// <summary> /// Emits a policy end. /// </summary> /// <param name="event">The event.</param> private void EmitJsonEnd(HclEvent @event) { GetTypedEvent <JsonEnd>(@event); this.isJson = false; this.indent = this.indents.Pop(); this.state = this.PopState(); this.WriteIndent(); this.WriteIndicator(")", false, false, false); if (this.state == EmitterState.Sequence) { this.Write(','); } }
/// <summary> /// Emits a scalar. /// </summary> /// <param name="event">The event.</param> private void EmitScalar(HclEvent @event) { var scalar = GetTypedEvent <Scalar>(@event); if (!this.isWhitespace) { this.Write(' '); } var value = NonInterpolatedTokenRegex.Replace(scalar.Value, match => "$" + match.Groups["token"].Value); if (value.Any(char.IsControl)) { // The Unicode standard classifies the characters \u000A (LF), \u000C (FF), and \u000D (CR) as control characters // Emit as here doc this.Write("<<-EOT"); foreach (var line in value.Split('\r', '\n')) { this.WriteIndent(); this.Write(line); } this.WriteIndent(); this.Write("EOT"); } else { if (scalar.IsQuoted) { this.Write('"'); } this.Write(value); if (scalar.IsQuoted) { this.Write('"'); } } if (this.state == EmitterState.Sequence) { this.Write(','); } this.isWhitespace = false; }
/// <summary> /// Emits the specified event. /// </summary> /// <param name="event">The event.</param> public void Emit(HclEvent @event) { switch (@event) { case JsonStart _: this.IsJson = true; break; case JsonEnd _: this.IsJson = false; break; } this.Emitter.Emit(@event); }
/// <summary> /// Emits a mapping end. /// </summary> /// <param name="event">The event.</param> private void EmitMappingEnd(HclEvent @event) { GetTypedEvent <MappingEnd>(@event); if (this.state != EmitterState.Resource) { this.state = this.PopState(); } this.indent = this.indents.Pop(); this.WriteIndent(); this.WriteIndicator("}", false, false, true); var mappingStart = this.events.Peek() as MappingStart; // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (this.state) { case EmitterState.BlockList when mappingStart != null: // Next element in a block list this.WriteIndent(); this.indent = this.indents.Pop(); this.EmitMappingKey(this.currentBlockKey); this.IncreaseIndent(); return; case EmitterState.Sequence when mappingStart != null: this.Write(','); this.WriteIndent(); break; } }
/// <summary> /// Determines when the end of a nested block is found in the event queue (nesting level returns to zero) /// </summary> /// <param name="event">The event.</param> /// <returns><c>true</c> when end of nesting located.</returns> public bool Done(HclEvent @event) { this.level += @event.NestingIncrease; return(this.level == 0); }
/// <summary> /// Adds an object to the end of the queue. /// </summary> /// <param name="event">The event.</param> public void Enqueue(HclEvent @event) { this.queue.AddLast(@event); }
/// <summary> /// Finds the specified event. /// </summary> /// <param name="event">The event.</param> /// <returns>The event; else <c>null</c> if not found.</returns> public LinkedListNode <HclEvent> Find(HclEvent @event) { return(this.queue.Find(@event)); }
/// <summary> /// Emits the next node. /// </summary> /// <param name="event">The event to write.</param> private void EmitNode(HclEvent @event) { // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (@event.Type) { case EventType.ResourceStart: this.EmitResourceStart(@event); break; case EventType.MappingKey: this.EmitMappingKey(@event); break; case EventType.ScalarValue: this.EmitScalarValue(@event); break; case EventType.SequenceStart: this.EmitSequenceStart(@event); break; case EventType.SequenceEnd: this.EmitSequenceEnd(@event); break; case EventType.MappingStart: this.EmitMappingStart(@event); break; case EventType.MappingEnd: this.EmitMappingEnd(@event); break; case EventType.JsonStart: this.EmitJsonStart(@event); break; case EventType.JsonEnd: this.EmitJsonEnd(@event); break; case EventType.ResourceEnd: this.indents.Clear(); this.lifecycleKeys.Clear(); this.blockKeys.Clear(); this.column = 0; this.indent = 0; this.resourceTraits = AwsSchema.TraitsAll; this.WriteBreak(); this.WriteBreak(); break; } }
/// <summary> /// Emits a mapping end. /// </summary> /// <param name="event">The event.</param> private void EmitMappingEnd(HclEvent @event) { GetTypedEvent <MappingEnd>(@event); if (this.state != EmitterState.Resource) { this.state = this.PopState(); } if (this.state == EmitterState.Resource && this.lifecycleKeys.Any()) { /* * Needs to go ahead of ResourceEnd which should be next * * BlockKey (lifecycle) * SequenceStart * MappingStart * MappingKey (ignore_changes ) * SequenceStart * Scalar (unquoted) * ... * SequenceEnd * MappingEnd * SequenceEnd * MappingEnd (end of resource) * */ var resourceEnd = this.events.Dequeue(); this.events.Enqueue( new MappingKey( "lifecycle", new AttributePath("lifecycle"), new ValueSchema { Optional = true, ConfigMode = SchemaConfigMode.SchemaConfigModeBlock, Type = SchemaValueType.TypeList })); this.events.Enqueue(new SequenceStart()); this.events.Enqueue(new MappingStart()); this.events.Enqueue( new MappingKey( "ignore_changes", new AttributePath("lifecycle.0.ignore_changes"), new ValueSchema { Optional = true, ConfigMode = SchemaConfigMode.SchemaConfigModeAuto })); this.events.Enqueue(new SequenceStart()); foreach (var key in this.lifecycleKeys) { this.events.Enqueue(new ScalarValue(key, false)); } this.events.Enqueue(new SequenceEnd()); this.events.Enqueue(new MappingEnd()); this.events.Enqueue(new SequenceEnd()); this.events.Enqueue(new MappingEnd()); this.events.Enqueue(resourceEnd); this.lifecycleKeys.Clear(); return; } this.indent = this.indents.Pop(); this.WriteIndent(); this.WriteIndicator("}", false, false, true); var mappingStart = this.events.Peek() as MappingStart; // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (this.state) { case EmitterState.BlockList when mappingStart != null: // Next element in a block list this.WriteIndent(); this.indent = this.indents.Pop(); this.EmitMappingKey(this.currentBlockKey); this.IncreaseIndent(); return; case EmitterState.Sequence when mappingStart != null: this.Write(','); this.WriteIndent(); break; } }