1import React from 'react';
2
3import MonoText from '../MonoText';
4import { EnumParameter, FunctionArgument, FunctionParameter, ObjectParameter } from './index.types';
5import { isCurrentPlatformSupported } from './utils';
6
7export default function FunctionSignature(props: {
8  namespace: string;
9  name: string;
10  parameters: FunctionParameter[];
11  args: FunctionArgument[];
12}) {
13  return <MonoText>{generateFunctionSignature(props)}</MonoText>;
14}
15
16export function generateFunctionSignature({
17  namespace,
18  name,
19  parameters,
20  args,
21}: {
22  namespace: string;
23  name: string;
24  parameters: FunctionParameter[];
25  args: FunctionArgument[];
26}) {
27  return `${namespace}.${name}(${argumentsToString(args, parameters)})`;
28}
29
30function argumentsToString(args: FunctionArgument[], parameters: FunctionParameter[]) {
31  return parameters
32    .map((parameter, idx) => {
33      if (!isCurrentPlatformSupported(parameter.platforms)) {
34        return;
35      }
36      switch (parameter.type) {
37        case 'object':
38          return convertObjectArgumentToString(args[idx], parameter);
39        case 'enum':
40          return convertEnumArgumentToString(args[idx], parameter);
41        case 'constant':
42          return parameter.name;
43        default:
44          return String(args[idx]);
45      }
46    })
47    .filter((arg) => !!arg) // filter out all void values
48    .join(', ');
49}
50
51function convertObjectArgumentToString(arg: FunctionArgument, parameter: ObjectParameter) {
52  const properties = parameter.properties
53    .map((property) => {
54      // skip object properties unsupported on the current platform
55      if (!isCurrentPlatformSupported(property.platforms)) {
56        return;
57      }
58
59      if (typeof arg !== 'object' || Array.isArray(arg) /** filter out tuples/arrays */) {
60        throw new Error(
61          `Value ${arg} is not an object. Expecting object for ${parameter.name} argument`
62        );
63      }
64
65      if (!(property.name in arg)) {
66        throw new Error(
67          `Property ${
68            property.name
69          } is missing in argument. Available parameter properties: ${parameter.properties
70            .map((p) => p.name)
71            .join(', ')} and argument properties: ${Object.keys(arg).join(', ')}`
72        );
73      }
74
75      const value = arg[property.name];
76
77      // skip `undefined` values
78      if (value === undefined) {
79        return;
80      }
81
82      if (property.type === 'enum') {
83        const stringArgument = convertEnumArgumentToString(value, property);
84        if (!stringArgument) return;
85        return `${property.name}: ${stringArgument}`;
86      }
87
88      return `${property.name}: ${property.type === 'string' ? `"${value}"` : value}`;
89    })
90    .filter((entry) => !!entry) // filter out all void values
91    .join(',\n  ');
92
93  return `{\n  ${properties}\n}`;
94}
95
96function convertEnumArgumentToString(arg: FunctionArgument, { name, values }: EnumParameter) {
97  // this should always find the current value for the enum, if failed something is messed up somewhere else
98  // eslint-disable-next-line no-case-declarations
99  const value = values.find(({ value }) =>
100    typeof value === 'object' && typeof arg === 'object'
101      ? JSON.stringify(value) === JSON.stringify(arg) // for tuple case
102      : value === arg
103  );
104  if (!value) {
105    return;
106  }
107  return value.name;
108}
109