1// Copyright (C) MongoDB, Inc. 2014-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package json
8
9import (
10	"fmt"
11	"reflect"
12)
13
14const CtorNumArgsErrorf = "expected %v argument%v to %v constructor, but %v received"
15
16// Transition functions for recognizing object constructors.
17// Adapted from encoding/json/scanner.go.
18
19// stateConstructor is the state after reading a constructor name.
20func stateConstructor(s *scanner, c int) int {
21	if c <= ' ' && isSpace(rune(c)) {
22		return scanSkipSpace
23	}
24	if c == '(' {
25		s.step = stateBeginCtorOrEmpty
26		s.pushParseState(parseCtorArg)
27		return scanBeginCtor
28	}
29	return s.error(c, "expected '('")
30}
31
32// stateBeginCtorOrEmpty is the state after reading `(`.
33func stateBeginCtorOrEmpty(s *scanner, c int) int {
34	if c <= ' ' && isSpace(rune(c)) {
35		return scanSkipSpace
36	}
37	if c == ')' {
38		return stateEndValue(s, c)
39	}
40	return stateBeginValue(s, c)
41}
42
43// ctor consumes a constructor from d.data[d.off-1:], given a type specification t.
44// the first byte of the constructor ('(') has been read already.
45func (d *decodeState) ctor(name string, t []reflect.Type) ([]reflect.Value, error) {
46	result := make([]reflect.Value, 0, len(t))
47
48	i := 0
49	for {
50		// Look ahead for ) - can only happen on first iteration.
51		op := d.scanWhile(scanSkipSpace)
52		if op == scanEndCtor {
53			break
54		}
55
56		// Back up so d.value can have the byte we just read.
57		d.off--
58		d.scan.undo(op)
59
60		if i < len(t) {
61			v := reflect.New(t[i]).Elem()
62
63			// Get argument of constructor
64			d.value(v)
65
66			result = append(result, v)
67			i++
68		}
69
70		// Next token must be , or ).
71		op = d.scanWhile(scanSkipSpace)
72		if op == scanEndCtor {
73			break
74		}
75		if op != scanCtorArg {
76			d.error(errPhase)
77		}
78	}
79
80	return result, ctorNumArgsMismatch(name, len(t), i)
81}
82
83// ctorInterface is like ctor but returns []interface{}.
84func (d *decodeState) ctorInterface() []interface{} {
85	var v = make([]interface{}, 0)
86	for {
87		// Look ahead for ) - can only happen on first iteration.
88		op := d.scanWhile(scanSkipSpace)
89		if op == scanEndCtor {
90			break
91		}
92
93		// Back up so d.value can have the byte we just read.
94		d.off--
95		d.scan.undo(op)
96
97		v = append(v, d.valueInterface(false))
98
99		// Next token must be , or ).
100		op = d.scanWhile(scanSkipSpace)
101		if op == scanEndCtor {
102			break
103		}
104		if op != scanCtorArg {
105			d.error(errPhase)
106		}
107	}
108	return v
109}
110
111// Returns a descriptive error message if the number of arguments given
112// to the constructor do not match what is expected.
113func ctorNumArgsMismatch(name string, expected, actual int) error {
114	if expected == actual {
115		return nil
116	}
117
118	quantifier := ""
119	if expected > 1 {
120		quantifier = "s"
121	}
122	return fmt.Errorf(CtorNumArgsErrorf, expected, quantifier, name, actual)
123}
124