export async function namer()

in packages/autorest.go/src/transform/namer.ts [44:307]


export async function namer(session: Session<CodeModel>) {
  const model = session.model;

  if (model.language.go) {
    // this looks like it already has data for this model.
    // send back an error
    session.error('bad flavor', ['go:1000', 'already-processed'], model.language.go);
    throw new Error('Go Namer Failed');
  }

  // copy all the .language.default data into .language.go
  cloneLanguageInfo(model);
  // default namespce to the output folder
  const outputFolder = await session.getValue<string>('output-folder');
  model.language.go!.packageName = packageNameFromOutputFolder(outputFolder);

  // default to the package name
  let stutteringPrefix = <string>model.language.go!.packageName;
  // if there's a well-known prefix, remove it
  if (stutteringPrefix.startsWith('arm')) {
    stutteringPrefix = stutteringPrefix.substring(3);
  } else if (stutteringPrefix.startsWith('az')) {
    stutteringPrefix = stutteringPrefix.substring(2);
  }
  // use the user-specified value if available
  stutteringPrefix = await session.getValue<string>('stutter', stutteringPrefix);
  stutteringPrefix = stutteringPrefix.toUpperCase();

  const specType = await session.getValue('openapi-type');
  model.language.go!.openApiType = specType;
  const azureARM = await session.getValue('azure-arm', false);
  model.language.go!.azureARM = azureARM;
  const headAsBoolean = await session.getValue('head-as-boolean', false);
  model.language.go!.headAsBoolean = headAsBoolean;
  const groupParameters = await session.getValue('group-parameters', true);
  model.language.go!.groupParameters = groupParameters;
  const honorBodyPlacement = await session.getValue('honor-body-placement', false);
  const rawJSONAsBytes = await session.getValue('rawjson-as-bytes', false);
  model.language.go!.rawJSONAsBytes = rawJSONAsBytes;
  const sliceElementsByValue = await session.getValue('slice-elements-byval', false);
  model.language.go!.sliceElementsByValue = sliceElementsByValue;

  const moduleVersion = await session.getValue('module-version', '');
  if (moduleVersion !== '') {
    model.language.go!.moduleVersion = moduleVersion;
  }

  const module = await session.getValue('module', '');
  if (module !== '') {
    model.language.go!.module = module;
  }
  model.language.go!.module = module;

  const containingModule = await session.getValue('containing-module', '');
  if (containingModule !== '' && module !== '') {
    throw new Error('--module and --containing-module are mutually exclusive');
  }
  model.language.go!.containingModule = containingModule;

  const singleClient = await session.getValue('single-client', false);
  model.language.go!.singleClient = singleClient;

  // fix up type names
  // final names are added to typeNames which is used to detect collisions
  const typeNames = new Set<string>();
  for (const obj of values(model.schemas.objects)) {
    obj.language.go!.name = ensureNameCase(obj.language.go!.name);
    typeNames.add(obj.language.go!.name);
  }

  // fix up enum type and value names and capitzalize acronyms
  const renameChoicesAndValues = function(choices?: Array<ChoiceSchema> | Array<SealedChoiceSchema>): void {
    if (!choices) {
      return;
    }
    for (const choice of choices) {
      choice.language.go!.name = ensureNameCase(choice.language.go!.name);
      typeNames.add(choice.language.go!.name);
      // add PossibleValues func name
      choice.language.go!.possibleValuesFunc = `Possible${choice.language.go!.name}Values`;
      for (const choiceValue of choice.choices) {
        const details = <Language>choiceValue.language.go;
        details.name = `${choice.language.go?.name}${ensureNameCase(details.name)}`;
        typeNames.add(details.name);
      }
    }
  };

  renameChoicesAndValues(model.schemas.choices);
  renameChoicesAndValues(model.schemas.sealedChoices);

  // fix stuttering type names
  const collisions = new Array<string>();

  const fixStutteringTypeName = function(details: Language): void {
    const originalName = details.name;
    details.name = trimPackagePrefix(stutteringPrefix, originalName);
    // if the type was renamed to remove stuttering, check if it collides with an existing type name
    if (details.name !== originalName && typeNames.has(details.name)) {
      collisions.push(`type ${originalName} was renamed to ${details.name} which collides with an existing type name`);
    }
  };

  for (const obj of values(model.schemas.objects)) {
    fixStutteringTypeName(obj.language.go!);
  }

  // to avoid breaking changes, this is opt-in
  if (await session.getValue('fix-const-stuttering', false)) {
    const fixStutteringForChoicesAndValues = function(choices?: Array<ChoiceSchema> | Array<SealedChoiceSchema>): void {
      if (!choices) {
        return;
      }
      for (const choice of choices) {
        fixStutteringTypeName(choice.language.go!);
        for (const choiceValue of values(choice.choices)) {
          const details = <Language>choiceValue.language.go;
          fixStutteringTypeName(details);
        }
      }
    };

    fixStutteringForChoicesAndValues(model.schemas.choices);
    fixStutteringForChoicesAndValues(model.schemas.sealedChoices);
  }

  if (collisions.length > 0) {
    throw new Error(collisions.join('\n'));
  }

  // fix property names and other bits
  for (const obj of values(model.schemas.objects)) {
    const details = <Language>obj.language.go;
    if (obj.discriminator) {
      // if this is a discriminator add the interface name
      details.discriminatorInterface = createPolymorphicInterfaceName(details.name);
      const discriminatorTypes = new Array<string>();
      discriminatorTypes.push('*' + details.name);
      for (const child of values(obj.discriminator.all)) {
        discriminatorTypes.push('*' + child.language.go!.name);
      }
      discriminatorTypes.sort();
      details.discriminatorTypes = discriminatorTypes;
    }
    for (const prop of values(obj.properties)) {
      const details = <Language>prop.language.go;
      details.name = ensureNameCase(details.name);
      if (hasAdditionalProperties(obj) && details.name === 'AdditionalProperties') {
        // this is the case where a type contains the generic additional properties
        // and also has a field named additionalProperties.  we rename the field.
        details.name = 'AdditionalProperties1';
      }
    }
    // adding this extension to a type will skip generatings its serde (marshalling/unmarshalling) methods
    if (obj.extensions?.['x-ms-go-omit-serde-methods']) {
      obj.language.go!.omitSerDeMethods = true;
    }
  }

  // fix up operation group names
  const groupNames = new Set<string>();
  for (const group of values(model.operationGroups)) {
    if (group.language.go!.name.length > 0) {
      group.language.go!.name = ensureNameCase(group.language.go!.name);
      groupNames.add(group.language.go!.name);
    }
  }

  // fix up any missing operation group names and add client names
  const fallbackGroupName = ensureNameCase(session.model.info.title);
  const clientNames = new Set<string>();
  for (const group of values(model.operationGroups)) {
    // use the swagger title as the default name for operation groups that don't specify a group name
    if (group.language.go!.name.length === 0) {
      if (groupNames.has(fallbackGroupName)) {
        throw new Error(`the fallback operation group name ${fallbackGroupName} collides with an existing group name`);
      }
      group.language.go!.name = fallbackGroupName;
    }
    group.language.go!.clientName = group.language.go!.name;
    // don't generate a name like FooClientClient
    if (!(<string>group.language.go!.clientName).endsWith('Client')) {
      group.language.go!.clientName = `${group.language.go!.name}Client`;
    }
    clientNames.add(group.language.go!.clientName);
  }

  // fix up stuttering client names and operation names
  for (const group of values(model.operationGroups)) {
    const groupDetails = <Language>group.language.go;
    const originalName = groupDetails.clientName;
    groupDetails.clientName = trimPackagePrefix(stutteringPrefix, originalName);
    // if the client was renamed to remove stuttering, check if it collides with an existing client
    if (groupDetails.clientName !== originalName && clientNames.has(groupDetails.clientName)) {
      throw new Error(`client ${originalName} was renamed to ${groupDetails.clientName} which collides with an existing client name`);
    }
    groupDetails.clientCtorName = `New${groupDetails.clientName}`;
    for (const op of values(group.operations)) {
      const details = <OperationNaming>op.language.go;
      // propagate these settings to each operation for ease of access
      details.azureARM = model.language.go!.azureARM;
      details.openApiType = model.language.go!.openApiType;
      details.name = ensureNameCase(details.name);
      // add the client name to the operation as it's needed all over the place
      details.clientName = groupDetails.clientName;
      for (const param of values(aggregateParameters(op))) {
        if (param.language.go!.name === '$host' || param.language.go!.name.toUpperCase() === 'URL') {
          param.language.go!.name = 'endpoint';
          continue;
        }
        if (!honorBodyPlacement) {
          const opMethod = op.requests![0].protocol.http!.method;
          if (param.protocol.http?.in === 'body' && (opMethod === HttpMethod.Patch || opMethod === HttpMethod.Put)) {
            // we enforce PATCH/PUT body parameters to be required.  do this before fixing up the parameter name
            param.required = true;
          }
        }
        const inParamGroup = (param.extensions?.['x-ms-parameter-grouping'] && groupParameters) || param.required !== true;
        const paramDetails = <Language>param.language.go;
        // if this is part of a param group struct then don't apply param naming rules to it
        paramDetails.name = ensureNameCase(paramDetails.name, !inParamGroup);
        // fix up any param group names
        if (param.extensions?.['x-ms-parameter-grouping'] && groupParameters) {
          if (param.extensions['x-ms-parameter-grouping'].name) {
            param.extensions['x-ms-parameter-grouping'].name = ensureNameCase(param.extensions['x-ms-parameter-grouping'].name);
          } else if (param.extensions['x-ms-parameter-grouping'].postfix) {
            param.extensions['x-ms-parameter-grouping'].postfix = ensureNameCase(param.extensions['x-ms-parameter-grouping'].postfix);
          }
        } else {
          // only escape the name if it's not in a parameter group struct
          paramDetails.name = getEscapedReservedName(paramDetails.name, 'Param');
        }
      }
      details.protocolNaming = new protocolMethods(details.name);
      if (op.language.go!.paging) {
        if (op.language.go!.paging.nextLinkName === '') {
          // fix up broken swaggers that incorrectly specify no next link name
          op.language.go!.paging.nextLinkName = null;
        }
        if (op.language.go!.paging.nextLinkName !== null) {
          // apply same naming logic as per struct fields
          op.language.go!.paging.nextLinkName = ensureNameCase(op.language.go!.paging.nextLinkName);
        }
        if (op.language.go!.paging.member) {
          op.language.go!.paging.member = uncapitalize(op.language.go!.paging.member);
        }
      }
      for (const resp of values(op.responses)) {
        for (const header of values(resp.protocol.http!.headers)) {
          const head = <HttpHeader>header;
          head.language.go!.name = ensureNameCase(head.language.go!.name);
        }
      }
    }
  }

  for (const globalParam of values(session.model.globalParameters)) {
    const details = <Language>globalParam.language.go;
    const inParamGroup = globalParam.extensions?.['x-ms-parameter-grouping'] && groupParameters;
    // if this is part of a param group struct then don't apply param naming rules to it
    details.name = getEscapedReservedName(ensureNameCase(details.name, !inParamGroup), 'Param');
  }
  return session;
}