import sys as _sys import re as _re import json as _json import yaml as _yaml import argparse as _argparse def convey( x, fs ): y = x for f in fs: y = f(y) return y def string_coin( template, arguments, options = None ): options = ( { "character_open": "{{", "character_close": "}}", "ignore_whitespaces": True, } | (options or {}) ) result = template for (key, value, ) in arguments.items(): pattern = ( _re.escape(options["character_open"]) + ("\s*" if options["ignore_whitespaces"] else "") + _re.escape(key) + ("\s*" if options["ignore_whitespaces"] else "") + _re.escape(options["character_close"]) ) result = _re.sub( pattern, value, result ) return result def file_read( path ): handle = open(path, "r") content = handle.read() handle.close() return content def handle_value( value, options = None ): options = ( { "indicator": "@", } | (options or {}) ) return ( file_read(value[len(options["indicator"]):]) if value.startswith(options["indicator"]) else value ) def data_from_file( path ): return ( {} if (path is None) else ( ( lambda content: ( _json.loads(content) if path.endswith(".json") else _yaml.safe_load(content) ) ) (file_read(path)) ) ) def data_from_adhoc_arguments( arguments ): return dict( map( lambda argument: ( ( lambda parts: ( parts[0], handle_value(parts[1]), ) ) (argument.split(":", 1)) ), (arguments or {}) ) ) def main( ): ## args argument_parser = _argparse.ArgumentParser( prog = "coin", formatter_class = _argparse.ArgumentDefaultsHelpFormatter, description = "transforms a template string to its refined version by substituting its placeholders with concrete values; example: »echo '{{flowers}} are {{color}}' | coin -a flowers:roses -a color:red«", ) argument_parser.add_argument( "-t", "--template-path", dest = "template_path", type = str, metavar = "", default = None, help = "default: read from stdin", ) argument_parser.add_argument( "-d", "--data-path", type = str, default = None, metavar = "", help = "path to JSON or YAML file, containing placeholder assignments (e.g. »{\"flowers\": \"roses\", \"color\": \"red\"}«)", ) argument_parser.add_argument( "-a", "--adhoc-argument", type = str, dest = "adhoc_arguments", action = "append", metavar = ":", help = "prefixing with '@' causes the value to be interpreted as the path to a text file, whose content shall be used", ) argument_parser.add_argument( "-o", "--character-open", type = str, default = "{{", metavar = "", help = "placeholder opening character", ) argument_parser.add_argument( "-c", "--character-close", type = str, default = "}}", metavar = "", help = "placeholder closing character", ) argument_parser.add_argument( "-w", "--heed-whitespaces", action = "store_true", help = "whether whitespace characters in the template string shall be heeded, i.e. not be ignored", ) args = argument_parser.parse_args() ## exec content_in = ( _sys.stdin.read() if (args.template_path is None) else file_read(args.template_path) ) data = ( data_from_file(args.data_path) | data_from_adhoc_arguments(args.adhoc_arguments) ) content_out = string_coin( content_in, data, { "character_open": args.character_open, "character_close": args.character_close, "ignore_whitespaces": (not args.heed_whitespaces), } ) _sys.stdout.write(content_out) main()