package tool // applyDefaults fills absent properties with their schema "default", recursing // into objects and arrays so nested defaults are honoured too. func applyDefaults(schema map[string]any, args map[string]any) { props, _ := schema["properties"].(map[string]any) for name, raw := range props { ps, ok := raw.(map[string]any) if !ok { continue } if rawDefault, has := ps["default"]; has { if _, present := args[name]; !present { args[name] = deepCopy(rawDefault) } } if val, present := args[name]; present { applyDefaultsToValue(ps, val) } } } // applyDefaultsToValue recurses into a present argument value to fill nested // defaults according to the sub-schema. func applyDefaultsToValue(schema map[string]any, val any) { switch v := val.(type) { case map[string]any: applyDefaults(schema, v) case []any: applyDefaultsToArray(schema, v) } } // applyDefaultsToArray fills defaults for array items, handling the schematic, // tuple-in-items and 2020-12 prefixItems forms. func applyDefaultsToArray(schema map[string]any, v []any) { switch items := schema["items"].(type) { case map[string]any: for _, item := range v { applyDefaultsToValue(items, item) } case []any: for i, item := range v { if i < len(items) { if is, ok := items[i].(map[string]any); ok { applyDefaultsToValue(is, item) } } } } if prefix, ok := schema["prefixItems"].([]any); ok { for i, item := range v { if i < len(prefix) { if is, ok := prefix[i].(map[string]any); ok { applyDefaultsToValue(is, item) } } } } } // deepCopy returns a structurally independent copy of a JSON value so defaults // taken from a compiled schema are not corrupted by later in-place coercion. func deepCopy(v any) any { switch vv := v.(type) { case map[string]any: out := make(map[string]any, len(vv)) for k, val := range vv { out[k] = deepCopy(val) } return out case []any: out := make([]any, len(vv)) for i, item := range vv { out[i] = deepCopy(item) } return out default: return vv } }