trun

Script for parsing any output. Yes, it is all it does.
git clone git://gtms.dev/trun
Log | Files | Refs | README | LICENSE

trun.lua (3848B)


      1 #!/usr/bin/env lua
      2 
      3 -- Version: 1.1.5 ( 09.08.2021 )
      4 --
      5 -- Usage
      6 -- <cmd> 1> >(trun <handler> <name>)
      7 --
      8 -- # Track run
      9 -- TrackRun parse stdin of command and cache status to file.
     10 -- Status is taken from handler module via `handle` function.
     11 --
     12 -- # Handler
     13 -- Handler is module that implements specific functions to catch some key
     14 -- moment of tracking. Every handler must implement 'handle' function that
     15 -- returns status for that line. It takes two arguments:
     16 -- 1. `userdata` is table for storing data between hooks
     17 -- 2. `line` is current line to get status from.
     18 --
     19 -- Handlers must have `lua` ext. and be inside specific folder. viz->#Config
     20 --
     21 -- ## Handle function
     22 -- handle function can return either table or string. When table is returned,
     23 -- every item will end up on new line in status_file so it can be easily used
     24 -- by other scripts.
     25 --
     26 -- ## Hooks
     27 -- There are these hook functions you can implement:
     28 -- - on_start(userdata): before first line is passed to handle function
     29 -- - on_update(userdata, line, status): when status change
     30 -- - on_status_test(userdata, status, lastStatus): custom status test
     31 -- - on_end(userdata): when script end
     32 --
     33 -- ## Userdata
     34 -- userdata has pre filled `name` key with value of trun name
     35 --
     36 -- # Config
     37 -- There are 2 ENV VAR you may want to set:
     38 -- - TRUN_HANDLERS_DIR: directory where handlers are stored
     39 local handlers_dir_def = os.getenv('XDG_CONFIG_HOME') .. '/trun'
     40 local handlers_dir = os.getenv('TRUN_HANDLERS_DIR') or handlers_dir_def
     41 -- - TRUN_STATUS_DIR: directory to store files with statuses
     42 local status_dir_def = os.getenv('XDG_CACHE_HOME') .. '/trun'
     43 local status_dir = os.getenv('TRUN_STATUS_DIR') or status_dir_def
     44 
     45 local handler_name = arg[1]
     46 if not handler_name then
     47   io.write('No handler provided!\n')
     48   os.exit(1)
     49 end
     50 local name = arg[2] or 'trun'
     51 
     52 -- load handler
     53 local handler_path = string.format('%s/?.lua', handlers_dir)
     54 package.path = handler_path .. ';' .. package.path
     55 local handler = require(handler_name)
     56 if not handler then
     57   io.write('handler "' .. handler_name .. '" not found!\n')
     58   os.exit(1)
     59 end
     60 local userdata = {name = name}
     61 
     62 -- status dir
     63 if not io.open(status_dir) then os.execute('mkdir ' .. status_dir) end
     64 
     65 -- status file
     66 local status_file = status_dir .. '/' .. name .. '.' .. handler_name
     67 local file = io.open(status_file, 'w')
     68 if not file then os.execute('touch ' .. status_file) end
     69 
     70 -- cleanup removes status file and notify handlers.
     71 local function cleanup()
     72   os.execute('rm ' .. status_file)
     73   if handler.on_end then handler.on_end(userdata) end
     74 end
     75 
     76 ------------------------------------------------------------------------------
     77 -- handling signals
     78 ------------------------------------------------------------------------------
     79 
     80 local function on_exit(signum)
     81   cleanup()
     82   os.exit(signum)
     83 end
     84 
     85 local signal = require 'posix.signal'
     86 if signal then
     87   signal.signal(signal.SIGINT, on_exit)
     88   signal.signal(signal.SIGTERM, on_exit)
     89   signal.signal(signal.SIGKILL, on_exit)
     90   signal.signal(signal.SIGHUP, on_exit)
     91 else
     92   io.write('For signal handle install luaposix (`luarocks install luaposix`).\n')
     93 end
     94 
     95 ------------------------------------------------------------------------------
     96 
     97 local status
     98 
     99 local function update_file(output)
    100   file = io.open(status_file, 'w')
    101   if type(output) == 'table' then
    102     file:write(table.concat(output, '\n'))
    103   else
    104     file:write(output)
    105   end
    106   file:close()
    107 end
    108 
    109 if handler.on_start then handler.on_start(userdata) end
    110 
    111 local lastStatus
    112 for line in io.lines() do
    113   lastStatus = status
    114   status = handler.handle(userdata, line)
    115 
    116   local status_test = handler.on_status_test or function(_, s, lastS) return s ~= lastS end
    117   if status and status_test(userdata, status, lastStatus) then
    118     update_file(status)
    119     if handler.on_update then handler.on_update(userdata, line, status) end
    120   end
    121 end
    122 
    123 cleanup()