github.com/kaptinlin/jsonschema@v0.4.6/format.go (about)

     1  package jsonschema
     2  
     3  // EvaluateFormat checks if the data conforms to the format specified in the schema.
     4  // According to the JSON Schema Draft 2020-12:
     5  //   - The "format" keyword defines the data format expected for a value.
     6  //   - The format must be a string that names a specific format which the value should conform to.
     7  //   - The function uses custom formats first, then falls back to the global `Formats` map.
     8  //   - If the format is not supported or not found, it may fall back to a no-op validation depending on configuration.
     9  //
    10  // This method ensures that data matches the expected format as specified in the schema.
    11  // It handles formats as annotations by default, but can assert format validation if configured.
    12  //
    13  // Reference: https://json-schema.org/draft/2020-12/json-schema-validation#name-format
    14  func evaluateFormat(schema *Schema, value interface{}) *EvaluationError {
    15  	if schema.Format == nil {
    16  		return nil
    17  	}
    18  
    19  	formatName := *schema.Format
    20  	var formatDef *FormatDef
    21  	var customValidator func(interface{}) bool
    22  
    23  	// 1. Check compiler-specific custom formats first
    24  	if schema.compiler != nil {
    25  		schema.compiler.customFormatsRW.RLock()
    26  		formatDef = schema.compiler.customFormats[formatName]
    27  		schema.compiler.customFormatsRW.RUnlock()
    28  	}
    29  
    30  	if formatDef != nil {
    31  		// Found in custom formats
    32  		if formatDef.Type != "" {
    33  			valueType := getDataType(value)
    34  			if !matchesType(valueType, formatDef.Type) {
    35  				return nil // Type doesn't match, so skip validation
    36  			}
    37  		}
    38  		customValidator = formatDef.Validate
    39  	} else if globalValidator, ok := Formats[formatName]; ok {
    40  		// Fallback to global formats
    41  		customValidator = globalValidator
    42  	}
    43  
    44  	// If a validator was found (either custom or global)
    45  	if customValidator != nil {
    46  		if !customValidator(value) {
    47  			if schema.compiler != nil && schema.compiler.AssertFormat {
    48  				return NewEvaluationError("format", "format_mismatch", "Value does not match format '{format}'", map[string]interface{}{"format": formatName})
    49  			}
    50  		}
    51  		return nil // Validation passed or not asserted
    52  	}
    53  
    54  	// If no validator was found and AssertFormat is true, fail
    55  	if schema.compiler != nil && schema.compiler.AssertFormat {
    56  		return NewEvaluationError("format", "unknown_format", "Unknown format '{format}'", map[string]interface{}{"format": formatName})
    57  	}
    58  
    59  	return nil // Default behavior: ignore unknown formats
    60  }
    61  
    62  // matchesType checks if a value type matches the required type
    63  func matchesType(valueType, requiredType string) bool {
    64  	if requiredType == "" {
    65  		return true // No type restriction
    66  	}
    67  
    68  	// Special handling: integer is also considered number
    69  	if requiredType == "number" && valueType == "integer" {
    70  		return true
    71  	}
    72  
    73  	return valueType == requiredType
    74  }