1*55e87721SMatt Gilbride# Copyright 2020 Google LLC 2*55e87721SMatt Gilbride# 3*55e87721SMatt Gilbride# Licensed under the Apache License, Version 2.0 (the "License"); 4*55e87721SMatt Gilbride# you may not use this file except in compliance with the License. 5*55e87721SMatt Gilbride# You may obtain a copy of the License at 6*55e87721SMatt Gilbride# 7*55e87721SMatt Gilbride# https://www.apache.org/licenses/LICENSE-2.0 8*55e87721SMatt Gilbride# 9*55e87721SMatt Gilbride# Unless required by applicable law or agreed to in writing, software 10*55e87721SMatt Gilbride# distributed under the License is distributed on an "AS IS" BASIS, 11*55e87721SMatt Gilbride# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*55e87721SMatt Gilbride# See the License for the specific language governing permissions and 13*55e87721SMatt Gilbride# limitations under the License. 14*55e87721SMatt Gilbride 15*55e87721SMatt Gilbrideimport glob 16*55e87721SMatt Gilbrideimport re 17*55e87721SMatt Gilbrideimport os 18*55e87721SMatt Gilbrideimport yaml 19*55e87721SMatt Gilbridefrom typing import List, Dict 20*55e87721SMatt Gilbridefrom synthtool.log import logger 21*55e87721SMatt Gilbride 22*55e87721SMatt Gilbride 23*55e87721SMatt Gilbridedef _read_sample_metadata_comment(sample_file: str) -> Dict: 24*55e87721SMatt Gilbride """Additional meta-information can be provided through embedded comments: 25*55e87721SMatt Gilbride 26*55e87721SMatt Gilbride // sample-metadata: 27*55e87721SMatt Gilbride // title: ACL (Access Control) 28*55e87721SMatt Gilbride // description: Demonstrates setting access control rules. 29*55e87721SMatt Gilbride // usage: node iam.js --help 30*55e87721SMatt Gilbride """ 31*55e87721SMatt Gilbride sample_metadata = {} # type: Dict[str, str] 32*55e87721SMatt Gilbride with open(sample_file) as f: 33*55e87721SMatt Gilbride contents = f.read() 34*55e87721SMatt Gilbride match = re.search( 35*55e87721SMatt Gilbride r"(?P<metadata>// *sample-metadata:([^\n]+|\n//)+)", contents, re.DOTALL 36*55e87721SMatt Gilbride ) 37*55e87721SMatt Gilbride if match: 38*55e87721SMatt Gilbride # the metadata yaml is stored in a comments, remove the 39*55e87721SMatt Gilbride # prefix so that we can parse the yaml contained. 40*55e87721SMatt Gilbride sample_metadata_string = re.sub(r"((#|//) ?)", "", match.group("metadata")) 41*55e87721SMatt Gilbride try: 42*55e87721SMatt Gilbride sample_metadata = yaml.load( 43*55e87721SMatt Gilbride sample_metadata_string, Loader=yaml.SafeLoader 44*55e87721SMatt Gilbride )["sample-metadata"] 45*55e87721SMatt Gilbride except yaml.scanner.ScannerError: 46*55e87721SMatt Gilbride # warn and continue on bad metadata 47*55e87721SMatt Gilbride logger.warning(f"bad metadata detected in {sample_file}") 48*55e87721SMatt Gilbride return sample_metadata 49*55e87721SMatt Gilbride 50*55e87721SMatt Gilbride 51*55e87721SMatt Gilbridedef _sample_metadata(file: str) -> Dict[str, str]: 52*55e87721SMatt Gilbride metadata = { 53*55e87721SMatt Gilbride "title": _decamelize(os.path.splitext(os.path.basename(file))[0]), 54*55e87721SMatt Gilbride "file": file, 55*55e87721SMatt Gilbride } 56*55e87721SMatt Gilbride return {**metadata, **_read_sample_metadata_comment(file)} 57*55e87721SMatt Gilbride 58*55e87721SMatt Gilbride 59*55e87721SMatt Gilbridedef all_samples(sample_globs: List[str]) -> List[Dict[str, str]]: 60*55e87721SMatt Gilbride """Walks samples directory and builds up samples data-structure 61*55e87721SMatt Gilbride 62*55e87721SMatt Gilbride Args: 63*55e87721SMatt Gilbride sample_globs: (List[str]): List of path globs to search for samples 64*55e87721SMatt Gilbride 65*55e87721SMatt Gilbride Returns: 66*55e87721SMatt Gilbride A list of sample metadata in the format: 67*55e87721SMatt Gilbride { 68*55e87721SMatt Gilbride "title": "Requester Pays", 69*55e87721SMatt Gilbride "file": "samples/requesterPays.js" 70*55e87721SMatt Gilbride } 71*55e87721SMatt Gilbride The file path is the relative path from the repository root. 72*55e87721SMatt Gilbride """ 73*55e87721SMatt Gilbride files = [] 74*55e87721SMatt Gilbride for sample_glob in sample_globs: 75*55e87721SMatt Gilbride for file in glob.glob(sample_glob, recursive=True): 76*55e87721SMatt Gilbride files.append(file) 77*55e87721SMatt Gilbride return [_sample_metadata(file) for file in sorted(files)] 78*55e87721SMatt Gilbride 79*55e87721SMatt Gilbride 80*55e87721SMatt Gilbridedef _decamelize(value: str): 81*55e87721SMatt Gilbride """Parser to convert fooBar.js to Foo Bar.""" 82*55e87721SMatt Gilbride if not value: 83*55e87721SMatt Gilbride return "" 84*55e87721SMatt Gilbride str_decamelize = re.sub("^.", value[0].upper(), value) # apple -> Apple. 85*55e87721SMatt Gilbride str_decamelize = re.sub( 86*55e87721SMatt Gilbride "([A-Z]+)([A-Z])([a-z0-9])", r"\1 \2\3", str_decamelize 87*55e87721SMatt Gilbride ) # ACLBatman -> ACL Batman. 88*55e87721SMatt Gilbride return re.sub("([a-z0-9])([A-Z])", r"\1 \2", str_decamelize) # FooBar -> Foo Bar. 89