# function: Extract-PluginMeta
# x-poshcode-id: 6832
# x-archived: 2017-04-07T08:11:23
# x-published: 2017-04-04T18:22:00
#
# encoding: utf-8
# api: ps
# type: functions
# title: PMD extraction
# description: Provides a crude implementation of Plugin Meta Data extraction
# version: 0.3
# depends: powershell >= 2.0, regex
# category: io
# author: mario
# src: https://pypi.python.org/pypi/pluginconf/0.6.7
# license: Public domain
# config: -
# status: alpha
# priority: extra
#
# A little consistency always goes a long way. -- This sample comment is a basic
# version of PMD. The format is (1) fairly obvious, (2) easy to extract, (3) most
# compatible to implementations in other scripting languages (Python, Ruby, PHP,
)
#
# The advantage over hodgepodge comments becomes more striking the more scripts
# amass. Or once integration or packaging become an issue.
#
# This version uses a few crude regex methods to fetch fields and config options:
#
# · Extract-PluginMeta reads from a given file name.
# · Simply scans the topmost comment block.
# · Fields are basically YAML entries.
# · Key names are case-insensitive, meant to be message/rfc-style,
# but otherwise free-form and per-project customizable.
# · Wheres the config: field is a JSON-style list.
# · No dependency management or anything else provided here.
#
# The default field names are api:, type:, title:, description:, version:, category:
# and config:. Though they're mostly decoration, and it's encouraged to introduce
# new ones, depending on project and tool requirements.
#-- baseline plugin meta data support
function Extract-PluginMeta() {
<#
.SYNOPSIS
Extract plugin meta data from given filename
.DESCRIPTION
Reads specified file, returns hashtable for key:value entries
from topmost comment block. Prepares config{} dicts.
.PARAMETER fn
Script to read from
.OUTPUTS
Returns a HashTable of extracted field → values
.EXAMPLE
For instance to read a couple of "plugins" at once:
$menu = (Get-Item tools/*.ps1 | % { Extract-PluginMeta $_ })
Which builds a usable hashtable list from asorted scripts,
once they have been documented. Which thus eases processing:
Show-MenuCLI ($menu | % { $_.title -and $_.type -eq "inline" })
Or executing "init" plugins for instance:
$menu | ? { $_.api -eq "mytool" -and $_.type -eq "init" } | { . $_.fn }
.TEST
(Extract-PluginMeta $MyInvocation.MyCommand.Definition).version -eq "0.3"
.NOTES
Packs comment remainder as `doc` field.
#>
Param($fn, $meta=@{}, $cfg=@())
# read file
$str = Get-Content $fn | Out-String
# look for first comment block
if ($m = [regex]::match($str, '(?m)((?:^\s*[#]+.*$\n?)+)')) {
# remove leading #␣ from lines, then split remainder comment
$str = $m.groups[1] -replace "(?m)^\s*#[ \t]*", ""
$str, $doc = [regex]::split($str, '(?m)^$\n')
# find all `key:value` pairs
preg_match_all -rx "(?m)^([\w-]+):\s*(.*(?:[\r\n]*^\s+.*$)*)" -str $str | % { $meta[$_[1]] = $_[2].trim() }
# split out config: and crude-parse it (JSONish serialization)
preg_match_all -rx "\{(.+?)\}" -str $meta.config | % { $r = @{};
preg_match_all -rx "([\w]+)\s*[:=]\s*(?:[']?([^,;}]+)[']?)" -str $_[1] | % { $r[$_[1]] = $_[2] }; $cfg += $r;
}
# merge into hashtable
$meta.fn = $fn
$meta.doc = ($doc -join "`r`n")
$meta.config = $cfg
}
return $meta # or return as (New-Object PSCustomObject -Prop $meta)
}
#-- Regex/Select-String -Allmatches as nested array / a convenience shortcut / well, why, yes; it's a PHP-style function
function preg_match_all() {
Param($rx, $str)
$str | Select-String $rx -AllMatches | % { $_.Matches } | % { ,($_.Groups | % { $_.Value }) }
}