// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package jsonconfig import ( "github.com/opentofu/opentofu/internal/configs" "github.com/opentofu/opentofu/internal/configs/configschema" "github.com/opentofu/opentofu/internal/tofu" ) // MarshalSingleModule is a variant of [Marshal] that describes only a single // module, without any references to its child modules or associated provider // schemas. // // This uses only a subset of the typical configuration representation, due to // schema and child module information being unavailable: // - Module calls omit the "module" property that would normally describe the // content of the child module. // - Resource descriptions omit the "schema_version" property because no // schema-based information is included. // - Expression-related properties are omitted in all cases. Technically only // expressions passed to providers _need_ to be omitted, but for now we // just consistently omit all of them because that's an easier rule to // explain and avoids exposing what is and is not provider-based so that // we could potentially change those details in future. func MarshalSingleModule(m *configs.Module) ([]byte, error) { // Our shared codepaths are built to work with a full config tree rather // than a single module, so we'll construct a synthetic [configs.Config] // that only has a root module and then downstream shared functions will // use the nil-ness of the schemas argument to handle the special // treatments required in single-module mode. cfg := &configs.Config{ Module: m, // Everything else intentionally not populated because single module // mode should not attempt to access anything else. } return marshal(cfg, nil) } // inSingleModuleMode returns true if the given schema value indicates that // we should be rendering in "single module" mode, meaning that we're producing // a result for [MarshalSingleModule] rather than [Marshal]. // // Currently the rule is only that a nil schemas represents single-module mode; // this simple rule is factored out into this helper function only so we can // centralize it underneath this doc comment explaining the special convention. // // (This rather odd design is a consequence of how this code evolved; we // retrofitted the single-module mode later while using this strange treatment // to minimize the risk to the existing working codepaths. Maybe we'll change // the appoach to this in future; this is only an implementation detail // within this package so we'll be able to those changes without affecting // callers.) func inSingleModuleMode[S schemaObject](schema S) bool { return schema == nil } // mapSchema is a helper that uses the given function to transform the given // schema object only if it isn't nil, or immediately returns nil otherwise. // // This is part of our strategy to retrofit the single-module mode without // a risky refactor of the already-working code, intended to be used in // conjunction with [inSingleModuleMode] to smuggle the flag for whether we're // in that mode through the nil-ness of the schema objects. func mapSchema[In, Out schemaObject](schema In, f func(In) Out) Out { if schema == nil { return nil } return f(schema) } // schemaObject is a helper interface to allow [inSingleModuleMode] to be // generic over the different nilable schema types used by different parts // of the implementation in this package. type schemaObject interface { *tofu.Schemas | *configschema.Block }