// Copyright 2016-2019, Pulumi Corporation using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; namespace Pulumi.Serialization { internal interface IOutputCompletionSource { Type TargetType { get; } IOutput Output { get; } void TrySetException(Exception exception); void TrySetDefaultResult(bool isKnown); void SetStringValue(string value, bool isKnown); void SetValue(OutputData data); } internal class OutputCompletionSource : IOutputCompletionSource { private readonly ImmutableHashSet _resources; private readonly TaskCompletionSource> _taskCompletionSource; public readonly Output Output; public OutputCompletionSource(Resource? resource) { _resources = resource == null ? ImmutableHashSet.Empty : ImmutableHashSet.Create(resource); _taskCompletionSource = new TaskCompletionSource>(); Output = new Output(_taskCompletionSource.Task); } public System.Type TargetType => typeof(T); IOutput IOutputCompletionSource.Output => Output; public void SetStringValue(string value, bool isKnown) => _taskCompletionSource.SetResult(new OutputData( _resources, (T)(object)value, isKnown, isSecret: false)); public void SetValue(OutputData data) => _taskCompletionSource.SetResult(new OutputData( _resources.Union(data.Resources), (T)data.Value!, data.IsKnown, data.IsSecret)); public void TrySetDefaultResult(bool isKnown) => _taskCompletionSource.TrySetResult(new OutputData( _resources, default!, isKnown, isSecret: false)); public void TrySetException(Exception exception) => _taskCompletionSource.TrySetException(exception); } internal static class OutputCompletionSource { public static ImmutableDictionary InitializeOutputs(Resource resource) { var name = resource.GetResourceName(); var type = resource.GetResourceType(); var query = from property in resource.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) let attr = property.GetCustomAttribute() where attr != null select (property, attrName: attr?.Name); var result = ImmutableDictionary.CreateBuilder(); foreach (var (prop, attrName) in query.ToList()) { var propType = prop.PropertyType; var propFullName = $"[Output] {resource.GetType().FullName}.{prop.Name}"; if (!propType.IsConstructedGenericType || propType.GetGenericTypeDefinition() != typeof(Output<>)) { throw new InvalidOperationException($"{propFullName} was not an Output"); } var setMethod = prop.DeclaringType!.GetMethod("set_" + prop.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (setMethod == null) { throw new InvalidOperationException($"{propFullName} did not have a 'set' method"); } var outputTypeArg = propType.GenericTypeArguments.Single(); Converter.CheckTargetType(propFullName, outputTypeArg, new HashSet()); var ocsType = typeof(OutputCompletionSource<>).MakeGenericType(outputTypeArg); var ocsContructor = ocsType.GetConstructors().Single(); var completionSource = (IOutputCompletionSource)ocsContructor.Invoke(new[] { resource }); setMethod.Invoke(resource, new[] { completionSource.Output }); var outputName = attrName ?? prop.Name; result.Add(outputName, completionSource); } Log.Debug("Fields to assign: " + JsonSerializer.Serialize(result.Keys), resource); return result.ToImmutable(); } } }