private void EmitResource(JsonTextWriter jsonWriter, ResourceMetadata resource, ExpressionEmitter emitter) { jsonWriter.WriteStartObject(); // Note: conditions STACK with nesting. // // Children inherit the conditions of their parents, etc. This avoids a problem // where we emit a dependsOn to something that's not in the template, or not // being evaulated i the template. var conditions = new List <SyntaxBase>(); var loops = new List <(string name, ForSyntax @for, SyntaxBase?input)>(); var ancestors = this.context.SemanticModel.ResourceAncestors.GetAncestors(resource); foreach (var ancestor in ancestors) { if (ancestor.AncestorType == ResourceAncestorGraph.ResourceAncestorType.Nested && ancestor.Resource.Symbol.DeclaringResource.Value is IfConditionSyntax ifCondition) { conditions.Add(ifCondition.ConditionExpression); } if (ancestor.AncestorType == ResourceAncestorGraph.ResourceAncestorType.Nested && ancestor.Resource.Symbol.DeclaringResource.Value is ForSyntax @for) { loops.Add((ancestor.Resource.Symbol.Name, @for, null)); } } // Unwrap the 'real' resource body if there's a condition var body = resource.Symbol.DeclaringResource.Value; switch (body) { case IfConditionSyntax ifCondition: body = ifCondition.Body; conditions.Add(ifCondition.ConditionExpression); break; case ForSyntax @for: loops.Add((resource.Symbol.Name, @for, null)); if (@for.Body is IfConditionSyntax loopFilter) { body = loopFilter.Body; conditions.Add(loopFilter.ConditionExpression); } else { body = @for.Body; } break; } if (conditions.Count == 1) { emitter.EmitProperty("condition", conditions[0]); } else if (conditions.Count > 1) { var @operator = new BinaryOperationSyntax( conditions[0], SyntaxFactory.CreateToken(TokenType.LogicalAnd), conditions[1]); for (var i = 2; i < conditions.Count; i++) { @operator = new BinaryOperationSyntax( @operator, SyntaxFactory.CreateToken(TokenType.LogicalAnd), conditions[i]); } emitter.EmitProperty("condition", @operator); } if (loops.Count == 1) { var batchSize = GetBatchSize(resource.Symbol.DeclaringResource); emitter.EmitProperty("copy", () => emitter.EmitCopyObject(loops[0].name, loops[0].@for, loops[0].input, batchSize: batchSize)); } else if (loops.Count > 1) { throw new InvalidOperationException("nested loops are not supported"); } if (context.Settings.EnableSymbolicNames && resource.IsExistingResource) { jsonWriter.WritePropertyName("existing"); jsonWriter.WriteValue(true); } var importSymbol = context.SemanticModel.Root.ImportDeclarations.FirstOrDefault(i => resource.Type.DeclaringNamespace.AliasNameEquals(i.Name)); if (importSymbol is not null) { emitter.EmitProperty("import", importSymbol.Name); } if (resource.IsAzResource) { emitter.EmitProperty("type", resource.TypeReference.FormatType()); if (resource.TypeReference.ApiVersion is not null) { emitter.EmitProperty("apiVersion", resource.TypeReference.ApiVersion); } } else { emitter.EmitProperty("type", resource.TypeReference.FormatName()); } if (context.SemanticModel.EmitLimitationInfo.ResourceScopeData.TryGetValue(resource, out var scopeData)) { ScopeHelper.EmitResourceScopeProperties(context.SemanticModel, scopeData, emitter, body); } if (resource.IsAzResource) { emitter.EmitProperty(AzResourceTypeProvider.ResourceNamePropertyName, emitter.GetFullyQualifiedResourceName(resource)); emitter.EmitObjectProperties((ObjectSyntax)body, ResourcePropertiesToOmit.Add(AzResourceTypeProvider.ResourceNamePropertyName)); } else { jsonWriter.WritePropertyName("properties"); jsonWriter.WriteStartObject(); emitter.EmitObjectProperties((ObjectSyntax)body, ResourcePropertiesToOmit); jsonWriter.WriteEndObject(); } this.EmitDependsOn(jsonWriter, resource.Symbol, emitter, body); // Since we don't want to be mutating the body of the original ObjectSyntax, we create an placeholder body in place // and emit its properties to merge decorator properties. foreach (var(property, val) in AddDecoratorsToBody( resource.Symbol.DeclaringResource, SyntaxFactory.CreateObject(Enumerable.Empty <ObjectPropertySyntax>()), resource.Symbol.Type).ToNamedPropertyValueDictionary()) { emitter.EmitProperty(property, val); } jsonWriter.WriteEndObject(); }
private void EmitResource(JsonTextWriter jsonWriter, ResourceMetadata resource, ExpressionEmitter emitter) { jsonWriter.WriteStartObject(); // Note: conditions STACK with nesting. // // Children inherit the conditions of their parents, etc. This avoids a problem // where we emit a dependsOn to something that's not in the template, or not // being evaulated i the template. var conditions = new List <SyntaxBase>(); var loops = new List <(string name, ForSyntax @for, SyntaxBase?input)>(); var ancestors = this.context.SemanticModel.ResourceAncestors.GetAncestors(resource); foreach (var ancestor in ancestors) { if (ancestor.AncestorType == ResourceAncestorGraph.ResourceAncestorType.Nested && ancestor.Resource.Symbol.DeclaringResource.Value is IfConditionSyntax ifCondition) { conditions.Add(ifCondition.ConditionExpression); } if (ancestor.AncestorType == ResourceAncestorGraph.ResourceAncestorType.Nested && ancestor.Resource.Symbol.DeclaringResource.Value is ForSyntax @for) { loops.Add((ancestor.Resource.Symbol.Name, @for, null)); } } // Unwrap the 'real' resource body if there's a condition var body = resource.Symbol.DeclaringResource.Value; switch (body) { case IfConditionSyntax ifCondition: body = ifCondition.Body; conditions.Add(ifCondition.ConditionExpression); break; case ForSyntax @for: loops.Add((resource.Symbol.Name, @for, null)); if (@for.Body is IfConditionSyntax loopFilter) { body = loopFilter.Body; conditions.Add(loopFilter.ConditionExpression); } else { body = @for.Body; } break; } if (conditions.Count == 1) { emitter.EmitProperty("condition", conditions[0]); } else if (conditions.Count > 1) { var @operator = new BinaryOperationSyntax( conditions[0], SyntaxFactory.CreateToken(TokenType.LogicalAnd), conditions[1]); for (var i = 2; i < conditions.Count; i++) { @operator = new BinaryOperationSyntax( @operator, SyntaxFactory.CreateToken(TokenType.LogicalAnd), conditions[i]); } emitter.EmitProperty("condition", @operator); } if (loops.Count == 1) { var batchSize = GetBatchSize(resource.Symbol.DeclaringResource); emitter.EmitProperty("copy", () => emitter.EmitCopyObject(loops[0].name, loops[0].@for, loops[0].input, batchSize: batchSize)); } else if (loops.Count > 1) { throw new InvalidOperationException("nested loops are not supported"); } emitter.EmitProperty("type", resource.TypeReference.FullyQualifiedType); emitter.EmitProperty("apiVersion", resource.TypeReference.ApiVersion); if (context.SemanticModel.EmitLimitationInfo.ResourceScopeData.TryGetValue(resource, out var scopeData)) { ScopeHelper.EmitResourceScopeProperties(context.SemanticModel, scopeData, emitter, body); } emitter.EmitProperty("name", emitter.GetFullyQualifiedResourceName(resource)); if (context.Settings.EnableSymbolicNames && resource.IsExistingResource) { jsonWriter.WritePropertyName("existing"); jsonWriter.WriteValue(true); } body = AddDecoratorsToBody(resource.Symbol.DeclaringResource, (ObjectSyntax)body, resource.Type); emitter.EmitObjectProperties((ObjectSyntax)body, ResourcePropertiesToOmit); this.EmitDependsOn(jsonWriter, resource.Symbol, emitter, body); jsonWriter.WriteEndObject(); }