go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/duration.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package lucicfg 16 17 import ( 18 "fmt" 19 "time" 20 21 "go.starlark.net/starlark" 22 "go.starlark.net/syntax" 23 ) 24 25 var zero = starlark.MakeInt64(0) 26 27 // duration wraps an integer, making it a distinct integer-like type. 28 type duration struct { 29 starlark.Int // milliseconds 30 } 31 32 // Type returns 'duration', to make the type different from ints. 33 func (x duration) Type() string { 34 return "duration" 35 } 36 37 // String formats the duration using Go's time.Duration rules. 38 func (x duration) String() string { 39 ms, ok := x.Int64() 40 if !ok { 41 return "<invalid-duration>" // probably very-very large 42 } 43 return (time.Duration(ms) * time.Millisecond).String() 44 } 45 46 // Cmp makes durations comparable by comparing them as integers. 47 func (x duration) Cmp(y starlark.Value, depth int) (int, error) { 48 return x.Int.Cmp(y.(duration).Int, depth) 49 } 50 51 // Binary implements binary operations between durations and ints. 52 func (x duration) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) { 53 switch y := y.(type) { 54 case starlark.Int: 55 switch { 56 case op == syntax.STAR: 57 return duration{x.Int.Mul(y)}, nil 58 case (op == syntax.SLASH || op == syntax.SLASHSLASH) && side == starlark.Left: 59 return duration{x.Int.Div(y)}, nil 60 } 61 62 case duration: 63 switch { 64 case op == syntax.PLUS: 65 return duration{x.Int.Add(y.Int)}, nil 66 case op == syntax.MINUS && side == starlark.Left: 67 return duration{x.Int.Sub(y.Int)}, nil 68 case op == syntax.MINUS && side == starlark.Right: 69 return duration{y.Int.Sub(x.Int)}, nil 70 case (op == syntax.SLASH || op == syntax.SLASHSLASH) && side == starlark.Left: 71 return x.Int.Div(y.Int), nil 72 case (op == syntax.SLASH || op == syntax.SLASHSLASH) && side == starlark.Right: 73 return y.Int.Div(x.Int), nil 74 case (op == syntax.PERCENT) && side == starlark.Left: 75 return x.Int.Mod(y.Int), nil 76 case (op == syntax.PERCENT) && side == starlark.Right: 77 return y.Int.Mod(x.Int), nil 78 } 79 } 80 81 // All other combinations aren't supported. 82 return nil, nil 83 } 84 85 // Unary implements +-. 86 func (x duration) Unary(op syntax.Token) (starlark.Value, error) { 87 switch op { 88 case syntax.PLUS: 89 return x, nil 90 case syntax.MINUS: 91 return duration{zero.Sub(x.Int)}, nil 92 } 93 return nil, nil 94 } 95 96 func init() { 97 // make_duration(milliseconds) returns a 'duration' value. 98 declNative("make_duration", func(call nativeCall) (starlark.Value, error) { 99 var ms starlark.Int 100 if err := call.unpack(0, &ms); err != nil { 101 return nil, err 102 } 103 return duration{ms}, nil 104 }) 105 106 // epoch(layout, value, location) returns int epoch seconds for value parsed as a time per layout in location. 107 declNative("epoch", func(call nativeCall) (starlark.Value, error) { 108 var layout starlark.String 109 var value starlark.String 110 var location starlark.String 111 if err := call.unpack(3, &layout, &value, &location); err != nil { 112 return nil, err 113 } 114 loc, err := time.LoadLocation(location.GoString()) 115 if err != nil { 116 return nil, fmt.Errorf("time.epoch: %s", err) 117 } 118 t, err := time.ParseInLocation(layout.GoString(), value.GoString(), loc) 119 if err != nil { 120 return nil, fmt.Errorf("time.epoch: %s", err) 121 } 122 return starlark.MakeInt(int(t.UnixNano() / 1000000000)), nil 123 }) 124 }