github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/shared/web_features.go (about)

     1  // Copyright 2024 The WPT Dashboard Project. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package shared
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strings"
    14  )
    15  
    16  // WebFeaturesData is the public data type that represents the data parsed from
    17  // a given manifest file.
    18  type WebFeaturesData map[string]map[string]interface{}
    19  
    20  // errUnknownWebFeaturesManifestVersion indicates that the parser does not know how to parse
    21  // this version of the web features file.
    22  var errUnknownWebFeaturesManifestVersion = errors.New("unknown web features manifest version")
    23  
    24  // errBadWebFeaturesManifestJSON indicates that there was an error parsing the given
    25  // v1 manifest file.
    26  var errBadWebFeaturesManifestJSON = errors.New("invalid json when reading web features manifest")
    27  
    28  // errUnexpectedWebFeaturesManifestV1Format indicates that there was an error parsing the given
    29  // v1 manifest file.
    30  var errUnexpectedWebFeaturesManifestV1Format = errors.New("unexpected web features manifest v1 format")
    31  
    32  // TestMatchesWithWebFeature performs two checks.
    33  // If the given test path is present in the data. If not, return false
    34  // If it is present, check if the given web feature applies to that test.
    35  func (d WebFeaturesData) TestMatchesWithWebFeature(test, webFeature string) bool {
    36  	if len(d) == 0 {
    37  		return false
    38  	}
    39  	if webFeatures, ok := d[test]; ok {
    40  		_, found := webFeatures[strings.ToLower(webFeature)]
    41  
    42  		return found
    43  	}
    44  
    45  	return false
    46  }
    47  
    48  // webFeaturesManifestFile is the base format for any manifest file.
    49  type webFeaturesManifestFile struct {
    50  	Version int             `json:"version,omitempty"`
    51  	Data    json.RawMessage `json:"data,omitempty"`
    52  }
    53  
    54  // webFeaturesManifestV1Data represents the data section in the manifest file
    55  // given manifest version 1.
    56  // The data is a simple map.
    57  // The key is the web feature key
    58  // The value is a list of tests that are part of that web feature.
    59  type webFeaturesManifestV1Data map[string][]string
    60  
    61  // webFeaturesManifestJSONParser is parser that can interpret a Web Features Manifest in a JSON format.
    62  type webFeaturesManifestJSONParser struct{}
    63  
    64  // Parse parses a JSON file into a WebFeaturesData instance.
    65  func (p webFeaturesManifestJSONParser) Parse(_ context.Context, r io.ReadCloser) (WebFeaturesData, error) {
    66  	defer r.Close()
    67  	file := new(webFeaturesManifestFile)
    68  	err := json.NewDecoder(r).Decode(file)
    69  	if err != nil {
    70  		return nil, errors.Join(errBadWebFeaturesManifestJSON, err)
    71  	}
    72  
    73  	switch file.Version {
    74  	case 1:
    75  		data := new(webFeaturesManifestV1Data)
    76  		err = json.Unmarshal(file.Data, data)
    77  		if err != nil {
    78  			return nil, errors.Join(errUnexpectedWebFeaturesManifestV1Format, err)
    79  		}
    80  
    81  		return data.prepareTestWebFeatureFilter(), nil
    82  	}
    83  
    84  	return nil, fmt.Errorf("bad version %d %w", file.Version, errUnknownWebFeaturesManifestVersion)
    85  }
    86  
    87  // prepareTestWebFeatureFilter maps a MetadataResult test name to its web features.
    88  func (d webFeaturesManifestV1Data) prepareTestWebFeatureFilter() WebFeaturesData {
    89  	// Create a map where the value is effectively a set (map[string]interface{})
    90  	testToWebFeaturesMap := make(map[string]map[string]interface{})
    91  	for webFeature, tests := range d {
    92  		for _, test := range tests {
    93  			key := strings.ToLower(test)
    94  			value := strings.ToLower(webFeature)
    95  			if set, found := testToWebFeaturesMap[key]; found {
    96  				set[value] = nil
    97  				testToWebFeaturesMap[key] = set
    98  			} else {
    99  				testToWebFeaturesMap[key] = map[string]interface{}{value: nil}
   100  			}
   101  		}
   102  	}
   103  
   104  	return testToWebFeaturesMap
   105  }