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 }