github.com/hy3/cuto@v0.9.8-0.20160830082821-aa6652f877b7/message/variable.go (about) 1 // Copyright 2015 unirita Inc. 2 // Created 2015/04/10 honda 3 4 package message 5 6 import ( 7 "fmt" 8 "os" 9 "regexp" 10 "strconv" 11 "strings" 12 13 "github.com/unirita/cuto/utctime" 14 ) 15 16 const ( 17 plcMaster = 'M' 18 plcServant = 'S' 19 kndSys = 'S' 20 kndEnv = 'E' 21 kndJob = 'J' 22 kndTime = 'T' 23 ) 24 25 const minKeyLength = len(`$MEx$`) 26 const tagSeparator = `:` 27 28 // 変数名の解析結果を格納する構造体 29 type variable struct { 30 key string 31 Place byte 32 Kind byte 33 Name string 34 Tag string 35 } 36 37 // ジョブネットワーク変数の値を格納する構造体 38 type jobValue struct { 39 ID string 40 RC string 41 SD string 42 ED string 43 OUT string 44 } 45 46 var sysValues map[string]string 47 var jobValues map[string]*jobValue 48 49 func init() { 50 sysValues = make(map[string]string) 51 jobValues = make(map[string]*jobValue) 52 } 53 54 // システム変数の値を追加する。 55 func AddSysValue(name, tag, value string) { 56 fullName := fmt.Sprintf("%s%s%s", name, tagSeparator, tag) 57 sysValues[fullName] = value 58 } 59 60 // ジョブネットワーク変数の値を追加する。 61 func AddJobValue(name string, res *Response) { 62 j := new(jobValue) 63 j.ID = res.JID 64 j.RC = strconv.Itoa(res.RC) 65 j.SD = res.St 66 j.ED = res.Et 67 j.OUT = res.Var 68 69 jobValues[name] = j 70 } 71 72 // 文字列src内の変数を展開する。 73 // 展開処理のパラメータとして場所識別子placeと利用可能種別kindsを指定する。 74 func ExpandStringVars(src string, place byte, kinds ...byte) (string, error) { 75 if len(kinds) == 0 { 76 return ``, fmt.Errorf("Invalid kind of variable.") 77 } 78 79 kindptn := string(kinds) 80 pattern := fmt.Sprintf(`\$%c[%s](.+?)\$`, place, kindptn) 81 exp := regexp.MustCompile(pattern) 82 // FindAllStringの第二引数に負の値を指定するとマッチ数の上限が無限になる。 83 matches := exp.FindAllString(src, -1) 84 85 result := src 86 for _, m := range matches { 87 v := NewVariable(m) 88 if v == nil { 89 continue 90 } 91 92 val, err := v.Expand() 93 if err != nil { 94 return ``, err 95 } 96 result = strings.Replace(result, m, val, -1) 97 } 98 99 return result, nil 100 } 101 102 // 変数名を解析してvariable構造体を生成する。 103 func NewVariable(key string) *variable { 104 if len(key) < minKeyLength { 105 return nil 106 } 107 108 v := new(variable) 109 v.key = key 110 v.Place = key[1] 111 v.Kind = key[2] 112 113 if v.Place != plcMaster && v.Place != plcServant { 114 return nil 115 } 116 117 if v.Kind != kndSys && v.Kind != kndEnv && v.Kind != kndJob && v.Kind != kndTime { 118 return nil 119 } 120 121 fullName := key[3 : len(key)-1] 122 nameAndTag := strings.Split(fullName, tagSeparator) 123 switch len(nameAndTag) { 124 case 1: 125 v.Name = nameAndTag[0] 126 case 2: 127 v.Name = nameAndTag[0] 128 v.Tag = nameAndTag[1] 129 default: 130 return nil 131 } 132 133 return v 134 } 135 136 // 変数名の文字列表現を返す。 137 func (v *variable) String() string { 138 return v.key 139 } 140 141 // 変数を値に展開する。 142 func (v *variable) Expand() (string, error) { 143 switch v.Kind { 144 case kndSys: 145 return v.expandSys() 146 case kndEnv: 147 return v.expandEnv() 148 case kndJob: 149 return v.expandJob() 150 case kndTime: 151 return v.expandTime() 152 } 153 154 return ``, fmt.Errorf("Undefined variable[%s].", v) 155 } 156 157 func (v *variable) expandSys() (string, error) { 158 fullName := fmt.Sprintf("%s%s%s", v.Name, tagSeparator, v.Tag) 159 val, ok := sysValues[fullName] 160 if !ok { 161 return ``, fmt.Errorf("Undefined variable[%s].", v) 162 } 163 164 return val, nil 165 } 166 167 func (v *variable) expandEnv() (string, error) { 168 return os.Getenv(v.Name), nil 169 } 170 171 func (v *variable) expandJob() (string, error) { 172 if v.Place == plcServant { 173 return ``, fmt.Errorf("Cannot use job variable in servant.") 174 } 175 j, ok := jobValues[v.Name] 176 if !ok { 177 return ``, fmt.Errorf("Job[%s] is not executed yet.", v.Name) 178 } 179 180 switch v.Tag { 181 case `ID`: 182 return j.ID, nil 183 case `RC`: 184 return j.RC, nil 185 case `SD`: 186 t, err := utctime.Parse(utctime.Default, j.SD) 187 if err != nil { 188 return ``, fmt.Errorf("Cannot parse time string[%s].", j.SD) 189 } 190 return t.Format("$ST" + utctime.NoDelimiter + "$"), nil 191 case `ED`: 192 t, err := utctime.Parse(utctime.Default, j.ED) 193 if err != nil { 194 return ``, fmt.Errorf("Cannot parse time string[%s].", j.ED) 195 } 196 return t.Format("$ST" + utctime.NoDelimiter + "$"), nil 197 case `OUT`: 198 return j.OUT, nil 199 } 200 201 return ``, fmt.Errorf("Undefined variable[%s].", v) 202 } 203 204 func (v *variable) expandTime() (string, error) { 205 if v.Place == plcMaster { 206 return ``, fmt.Errorf("Cannot use time variable in master.") 207 } 208 209 t, err := utctime.Parse(utctime.NoDelimiter, v.Name) 210 if err != nil { 211 return ``, fmt.Errorf("Cannot parse time variable[%s]. Reason[%s]", v.Name, err) 212 } 213 214 return t.FormatLocaltime(utctime.Default), nil 215 }