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  }