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()