mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Added all required dependencies
This commit is contained in:
		
							
								
								
									
										21
									
								
								vendor/github.com/gogits/cron/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/gogits/cron/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| Copyright (C) 2012 Rob Figueiredo | ||||
| All Rights Reserved. | ||||
|  | ||||
| MIT LICENSE | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||
| this software and associated documentation files (the "Software"), to deal in | ||||
| the Software without restriction, including without limitation the rights to | ||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||
| the Software, and to permit persons to whom the Software is furnished to do so, | ||||
| subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
							
								
								
									
										2
									
								
								vendor/github.com/gogits/cron/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gogits/cron/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [](http://godoc.org/github.com/robfig/cron)  | ||||
| [](https://travis-ci.org/robfig/cron) | ||||
							
								
								
									
										27
									
								
								vendor/github.com/gogits/cron/constantdelay.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gogits/cron/constantdelay.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package cron | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". | ||||
| // It does not support jobs more frequent than once a second. | ||||
| type ConstantDelaySchedule struct { | ||||
| 	Delay time.Duration | ||||
| } | ||||
|  | ||||
| // Every returns a crontab Schedule that activates once every duration. | ||||
| // Delays of less than a second are not supported (will round up to 1 second). | ||||
| // Any fields less than a Second are truncated. | ||||
| func Every(duration time.Duration) ConstantDelaySchedule { | ||||
| 	if duration < time.Second { | ||||
| 		duration = time.Second | ||||
| 	} | ||||
| 	return ConstantDelaySchedule{ | ||||
| 		Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Next returns the next time this should be run. | ||||
| // This rounds so that the next activation time will be on the second. | ||||
| func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { | ||||
| 	return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) | ||||
| } | ||||
							
								
								
									
										243
									
								
								vendor/github.com/gogits/cron/cron.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								vendor/github.com/gogits/cron/cron.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| // This library implements a cron spec parser and runner.  See the README for | ||||
| // more details. | ||||
| package cron | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"runtime" | ||||
| 	"sort" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Cron keeps track of any number of entries, invoking the associated func as | ||||
| // specified by the schedule. It may be started, stopped, and the entries may | ||||
| // be inspected while running. | ||||
| type Cron struct { | ||||
| 	entries  []*Entry | ||||
| 	stop     chan struct{} | ||||
| 	add      chan *Entry | ||||
| 	snapshot chan []*Entry | ||||
| 	running  bool | ||||
| 	ErrorLog *log.Logger | ||||
| } | ||||
|  | ||||
| // Job is an interface for submitted cron jobs. | ||||
| type Job interface { | ||||
| 	Run() | ||||
| } | ||||
|  | ||||
| // The Schedule describes a job's duty cycle. | ||||
| type Schedule interface { | ||||
| 	// Return the next activation time, later than the given time. | ||||
| 	// Next is invoked initially, and then each time the job is run. | ||||
| 	Next(time.Time) time.Time | ||||
| } | ||||
|  | ||||
| // Entry consists of a schedule and the func to execute on that schedule. | ||||
| type Entry struct { | ||||
| 	Description string | ||||
| 	Spec        string | ||||
|  | ||||
| 	// The schedule on which this job should be run. | ||||
| 	Schedule Schedule | ||||
|  | ||||
| 	// The next time the job will run. This is the zero time if Cron has not been | ||||
| 	// started or this entry's schedule is unsatisfiable | ||||
| 	Next time.Time | ||||
|  | ||||
| 	// The last time this job was run. This is the zero time if the job has never | ||||
| 	// been run. | ||||
| 	Prev time.Time | ||||
|  | ||||
| 	// The Job to run. | ||||
| 	Job Job | ||||
|  | ||||
| 	ExecTimes int // Execute times count. | ||||
| } | ||||
|  | ||||
| // byTime is a wrapper for sorting the entry array by time | ||||
| // (with zero time at the end). | ||||
| type byTime []*Entry | ||||
|  | ||||
| func (s byTime) Len() int      { return len(s) } | ||||
| func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | ||||
| func (s byTime) Less(i, j int) bool { | ||||
| 	// Two zero times should return false. | ||||
| 	// Otherwise, zero is "greater" than any other time. | ||||
| 	// (To sort it at the end of the list.) | ||||
| 	if s[i].Next.IsZero() { | ||||
| 		return false | ||||
| 	} | ||||
| 	if s[j].Next.IsZero() { | ||||
| 		return true | ||||
| 	} | ||||
| 	return s[i].Next.Before(s[j].Next) | ||||
| } | ||||
|  | ||||
| // New returns a new Cron job runner. | ||||
| func New() *Cron { | ||||
| 	return &Cron{ | ||||
| 		entries:  nil, | ||||
| 		add:      make(chan *Entry), | ||||
| 		stop:     make(chan struct{}), | ||||
| 		snapshot: make(chan []*Entry), | ||||
| 		running:  false, | ||||
| 		ErrorLog: nil, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // A wrapper that turns a func() into a cron.Job | ||||
| type FuncJob func() | ||||
|  | ||||
| func (f FuncJob) Run() { f() } | ||||
|  | ||||
| // AddFunc adds a func to the Cron to be run on the given schedule. | ||||
| func (c *Cron) AddFunc(desc, spec string, cmd func()) (*Entry, error) { | ||||
| 	return c.AddJob(desc, spec, FuncJob(cmd)) | ||||
| } | ||||
|  | ||||
| // AddJob adds a Job to the Cron to be run on the given schedule. | ||||
| func (c *Cron) AddJob(desc, spec string, cmd Job) (*Entry, error) { | ||||
| 	schedule, err := Parse(spec) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return c.Schedule(desc, spec, schedule, cmd), nil | ||||
| } | ||||
|  | ||||
| // Schedule adds a Job to the Cron to be run on the given schedule. | ||||
| func (c *Cron) Schedule(desc, spec string, schedule Schedule, cmd Job) *Entry { | ||||
| 	entry := &Entry{ | ||||
| 		Description: desc, | ||||
| 		Spec:        spec, | ||||
| 		Schedule:    schedule, | ||||
| 		Job:         cmd, | ||||
| 	} | ||||
| 	if c.running { | ||||
| 		c.add <- entry | ||||
| 	} else { | ||||
| 		c.entries = append(c.entries, entry) | ||||
| 	} | ||||
| 	return entry | ||||
| } | ||||
|  | ||||
| // Entries returns a snapshot of the cron entries. | ||||
| func (c *Cron) Entries() []*Entry { | ||||
| 	if c.running { | ||||
| 		c.snapshot <- nil | ||||
| 		x := <-c.snapshot | ||||
| 		return x | ||||
| 	} | ||||
| 	return c.entrySnapshot() | ||||
| } | ||||
|  | ||||
| // Start the cron scheduler in its own go-routine, or no-op if already started. | ||||
| func (c *Cron) Start() { | ||||
| 	if c.running { | ||||
| 		return | ||||
| 	} | ||||
| 	c.running = true | ||||
| 	go c.run() | ||||
| } | ||||
|  | ||||
| func (c *Cron) runWithRecovery(j Job) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			const size = 64 << 10 | ||||
| 			buf := make([]byte, size) | ||||
| 			buf = buf[:runtime.Stack(buf, false)] | ||||
| 			c.logf("cron: panic running job: %v\n%s", r, buf) | ||||
| 		} | ||||
| 	}() | ||||
| 	j.Run() | ||||
| } | ||||
|  | ||||
| // Run the scheduler.. this is private just due to the need to synchronize | ||||
| // access to the 'running' state variable. | ||||
| func (c *Cron) run() { | ||||
| 	// Figure out the next activation times for each entry. | ||||
| 	now := time.Now().Local() | ||||
| 	for _, entry := range c.entries { | ||||
| 		entry.Next = entry.Schedule.Next(now) | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		// Determine the next entry to run. | ||||
| 		sort.Sort(byTime(c.entries)) | ||||
|  | ||||
| 		var effective time.Time | ||||
| 		if len(c.entries) == 0 || c.entries[0].Next.IsZero() { | ||||
| 			// If there are no entries yet, just sleep - it still handles new entries | ||||
| 			// and stop requests. | ||||
| 			effective = now.AddDate(10, 0, 0) | ||||
| 		} else { | ||||
| 			effective = c.entries[0].Next | ||||
| 		} | ||||
|  | ||||
| 		timer := time.NewTimer(effective.Sub(now)) | ||||
| 		select { | ||||
| 		case now = <-timer.C: | ||||
| 			// Run every entry whose next time was this effective time. | ||||
| 			for _, e := range c.entries { | ||||
| 				if e.Next != effective { | ||||
| 					break | ||||
| 				} | ||||
| 				go c.runWithRecovery(e.Job) | ||||
| 				e.ExecTimes++ | ||||
| 				e.Prev = e.Next | ||||
| 				e.Next = e.Schedule.Next(now) | ||||
| 			} | ||||
| 			continue | ||||
|  | ||||
| 		case newEntry := <-c.add: | ||||
| 			c.entries = append(c.entries, newEntry) | ||||
| 			newEntry.Next = newEntry.Schedule.Next(time.Now().Local()) | ||||
|  | ||||
| 		case <-c.snapshot: | ||||
| 			c.snapshot <- c.entrySnapshot() | ||||
|  | ||||
| 		case <-c.stop: | ||||
| 			timer.Stop() | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// 'now' should be updated after newEntry and snapshot cases. | ||||
| 		now = time.Now().Local() | ||||
| 		timer.Stop() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Logs an error to stderr or to the configured error log | ||||
| func (c *Cron) logf(format string, args ...interface{}) { | ||||
| 	if c.ErrorLog != nil { | ||||
| 		c.ErrorLog.Printf(format, args...) | ||||
| 	} else { | ||||
| 		log.Printf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Stop stops the cron scheduler if it is running; otherwise it does nothing. | ||||
| func (c *Cron) Stop() { | ||||
| 	if !c.running { | ||||
| 		return | ||||
| 	} | ||||
| 	c.stop <- struct{}{} | ||||
| 	c.running = false | ||||
| } | ||||
|  | ||||
| // entrySnapshot returns a copy of the current cron entry list. | ||||
| func (c *Cron) entrySnapshot() []*Entry { | ||||
| 	entries := []*Entry{} | ||||
| 	for _, e := range c.entries { | ||||
| 		entries = append(entries, &Entry{ | ||||
| 			Description: e.Description, | ||||
| 			Spec:        e.Spec, | ||||
| 			Schedule:    e.Schedule, | ||||
| 			Next:        e.Next, | ||||
| 			Prev:        e.Prev, | ||||
| 			Job:         e.Job, | ||||
| 			ExecTimes:   e.ExecTimes, | ||||
| 		}) | ||||
| 	} | ||||
| 	return entries | ||||
| } | ||||
							
								
								
									
										129
									
								
								vendor/github.com/gogits/cron/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								vendor/github.com/gogits/cron/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| /* | ||||
| Package cron implements a cron spec parser and job runner. | ||||
|  | ||||
| Usage | ||||
|  | ||||
| Callers may register Funcs to be invoked on a given schedule.  Cron will run | ||||
| them in their own goroutines. | ||||
|  | ||||
| 	c := cron.New() | ||||
| 	c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") }) | ||||
| 	c.AddFunc("@hourly",      func() { fmt.Println("Every hour") }) | ||||
| 	c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") }) | ||||
| 	c.Start() | ||||
| 	.. | ||||
| 	// Funcs are invoked in their own goroutine, asynchronously. | ||||
| 	... | ||||
| 	// Funcs may also be added to a running Cron | ||||
| 	c.AddFunc("@daily", func() { fmt.Println("Every day") }) | ||||
| 	.. | ||||
| 	// Inspect the cron job entries' next and previous run times. | ||||
| 	inspect(c.Entries()) | ||||
| 	.. | ||||
| 	c.Stop()  // Stop the scheduler (does not stop any jobs already running). | ||||
|  | ||||
| CRON Expression Format | ||||
|  | ||||
| A cron expression represents a set of times, using 6 space-separated fields. | ||||
|  | ||||
| 	Field name   | Mandatory? | Allowed values  | Allowed special characters | ||||
| 	----------   | ---------- | --------------  | -------------------------- | ||||
| 	Seconds      | Yes        | 0-59            | * / , - | ||||
| 	Minutes      | Yes        | 0-59            | * / , - | ||||
| 	Hours        | Yes        | 0-23            | * / , - | ||||
| 	Day of month | Yes        | 1-31            | * / , - ? | ||||
| 	Month        | Yes        | 1-12 or JAN-DEC | * / , - | ||||
| 	Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ? | ||||
|  | ||||
| Note: Month and Day-of-week field values are case insensitive.  "SUN", "Sun", | ||||
| and "sun" are equally accepted. | ||||
|  | ||||
| Special Characters | ||||
|  | ||||
| Asterisk ( * ) | ||||
|  | ||||
| The asterisk indicates that the cron expression will match for all values of the | ||||
| field; e.g., using an asterisk in the 5th field (month) would indicate every | ||||
| month. | ||||
|  | ||||
| Slash ( / ) | ||||
|  | ||||
| Slashes are used to describe increments of ranges. For example 3-59/15 in the | ||||
| 1st field (minutes) would indicate the 3rd minute of the hour and every 15 | ||||
| minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", | ||||
| that is, an increment over the largest possible range of the field.  The form | ||||
| "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the | ||||
| increment until the end of that specific range.  It does not wrap around. | ||||
|  | ||||
| Comma ( , ) | ||||
|  | ||||
| Commas are used to separate items of a list. For example, using "MON,WED,FRI" in | ||||
| the 5th field (day of week) would mean Mondays, Wednesdays and Fridays. | ||||
|  | ||||
| Hyphen ( - ) | ||||
|  | ||||
| Hyphens are used to define ranges. For example, 9-17 would indicate every | ||||
| hour between 9am and 5pm inclusive. | ||||
|  | ||||
| Question mark ( ? ) | ||||
|  | ||||
| Question mark may be used instead of '*' for leaving either day-of-month or | ||||
| day-of-week blank. | ||||
|  | ||||
| Predefined schedules | ||||
|  | ||||
| You may use one of several pre-defined schedules in place of a cron expression. | ||||
|  | ||||
| 	Entry                  | Description                                | Equivalent To | ||||
| 	-----                  | -----------                                | ------------- | ||||
| 	@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 * | ||||
| 	@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * * | ||||
| 	@weekly                | Run once a week, midnight on Sunday        | 0 0 0 * * 0 | ||||
| 	@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * * | ||||
| 	@hourly                | Run once an hour, beginning of hour        | 0 0 * * * * | ||||
|  | ||||
| Intervals | ||||
|  | ||||
| You may also schedule a job to execute at fixed intervals.  This is supported by | ||||
| formatting the cron spec like this: | ||||
|  | ||||
|     @every <duration> | ||||
|  | ||||
| where "duration" is a string accepted by time.ParseDuration | ||||
| (http://golang.org/pkg/time/#ParseDuration). | ||||
|  | ||||
| For example, "@every 1h30m10s" would indicate a schedule that activates every | ||||
| 1 hour, 30 minutes, 10 seconds. | ||||
|  | ||||
| Note: The interval does not take the job runtime into account.  For example, | ||||
| if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, | ||||
| it will have only 2 minutes of idle time between each run. | ||||
|  | ||||
| Time zones | ||||
|  | ||||
| All interpretation and scheduling is done in the machine's local time zone (as | ||||
| provided by the Go time package (http://www.golang.org/pkg/time). | ||||
|  | ||||
| Be aware that jobs scheduled during daylight-savings leap-ahead transitions will | ||||
| not be run! | ||||
|  | ||||
| Thread safety | ||||
|  | ||||
| Since the Cron service runs concurrently with the calling code, some amount of | ||||
| care must be taken to ensure proper synchronization. | ||||
|  | ||||
| All cron methods are designed to be correctly synchronized as long as the caller | ||||
| ensures that invocations have a clear happens-before ordering between them. | ||||
|  | ||||
| Implementation | ||||
|  | ||||
| Cron entries are stored in an array, sorted by their next activation time.  Cron | ||||
| sleeps until the next job is due to be run. | ||||
|  | ||||
| Upon waking: | ||||
|  - it runs each entry that is active on that second | ||||
|  - it calculates the next run times for the jobs that were run | ||||
|  - it re-sorts the array of entries by next activation time. | ||||
|  - it goes to sleep until the soonest job. | ||||
| */ | ||||
| package cron | ||||
							
								
								
									
										234
									
								
								vendor/github.com/gogits/cron/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								vendor/github.com/gogits/cron/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| package cron | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Parse returns a new crontab schedule representing the given spec. | ||||
| // It returns a descriptive error if the spec is not valid. | ||||
| // | ||||
| // It accepts | ||||
| //   - Full crontab specs, e.g. "* * * * * ?" | ||||
| //   - Descriptors, e.g. "@midnight", "@every 1h30m" | ||||
| func Parse(spec string) (_ Schedule, err error) { | ||||
| 	// Convert panics into errors | ||||
| 	defer func() { | ||||
| 		if recovered := recover(); recovered != nil { | ||||
| 			err = fmt.Errorf("%v", recovered) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	if spec[0] == '@' { | ||||
| 		return parseDescriptor(spec), nil | ||||
| 	} | ||||
|  | ||||
| 	// Split on whitespace.  We require 5 or 6 fields. | ||||
| 	// (second) (minute) (hour) (day of month) (month) (day of week, optional) | ||||
| 	fields := strings.Fields(spec) | ||||
| 	if len(fields) != 5 && len(fields) != 6 { | ||||
| 		log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec) | ||||
| 	} | ||||
|  | ||||
| 	// If a sixth field is not provided (DayOfWeek), then it is equivalent to star. | ||||
| 	if len(fields) == 5 { | ||||
| 		fields = append(fields, "*") | ||||
| 	} | ||||
|  | ||||
| 	schedule := &SpecSchedule{ | ||||
| 		Second: getField(fields[0], seconds), | ||||
| 		Minute: getField(fields[1], minutes), | ||||
| 		Hour:   getField(fields[2], hours), | ||||
| 		Dom:    getField(fields[3], dom), | ||||
| 		Month:  getField(fields[4], months), | ||||
| 		Dow:    getField(fields[5], dow), | ||||
| 	} | ||||
|  | ||||
| 	return schedule, nil | ||||
| } | ||||
|  | ||||
| // getField returns an Int with the bits set representing all of the times that | ||||
| // the field represents.  A "field" is a comma-separated list of "ranges". | ||||
| func getField(field string, r bounds) uint64 { | ||||
| 	// list = range {"," range} | ||||
| 	var bits uint64 | ||||
| 	ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' }) | ||||
| 	for _, expr := range ranges { | ||||
| 		bits |= getRange(expr, r) | ||||
| 	} | ||||
| 	return bits | ||||
| } | ||||
|  | ||||
| // getRange returns the bits indicated by the given expression: | ||||
| //   number | number "-" number [ "/" number ] | ||||
| func getRange(expr string, r bounds) uint64 { | ||||
|  | ||||
| 	var ( | ||||
| 		start, end, step uint | ||||
| 		rangeAndStep     = strings.Split(expr, "/") | ||||
| 		lowAndHigh       = strings.Split(rangeAndStep[0], "-") | ||||
| 		singleDigit      = len(lowAndHigh) == 1 | ||||
| 	) | ||||
|  | ||||
| 	var extra_star uint64 | ||||
| 	if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" { | ||||
| 		start = r.min | ||||
| 		end = r.max | ||||
| 		extra_star = starBit | ||||
| 	} else { | ||||
| 		start = parseIntOrName(lowAndHigh[0], r.names) | ||||
| 		switch len(lowAndHigh) { | ||||
| 		case 1: | ||||
| 			end = start | ||||
| 		case 2: | ||||
| 			end = parseIntOrName(lowAndHigh[1], r.names) | ||||
| 		default: | ||||
| 			log.Panicf("Too many hyphens: %s", expr) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch len(rangeAndStep) { | ||||
| 	case 1: | ||||
| 		step = 1 | ||||
| 	case 2: | ||||
| 		step = mustParseInt(rangeAndStep[1]) | ||||
|  | ||||
| 		// Special handling: "N/step" means "N-max/step". | ||||
| 		if singleDigit { | ||||
| 			end = r.max | ||||
| 		} | ||||
| 	default: | ||||
| 		log.Panicf("Too many slashes: %s", expr) | ||||
| 	} | ||||
|  | ||||
| 	if start < r.min { | ||||
| 		log.Panicf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr) | ||||
| 	} | ||||
| 	if end > r.max { | ||||
| 		log.Panicf("End of range (%d) above maximum (%d): %s", end, r.max, expr) | ||||
| 	} | ||||
| 	if start > end { | ||||
| 		log.Panicf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr) | ||||
| 	} | ||||
| 	if step == 0 { | ||||
| 		log.Panicf("Step of range should be a positive number: %s", expr) | ||||
| 	} | ||||
|  | ||||
| 	return getBits(start, end, step) | extra_star | ||||
| } | ||||
|  | ||||
| // parseIntOrName returns the (possibly-named) integer contained in expr. | ||||
| func parseIntOrName(expr string, names map[string]uint) uint { | ||||
| 	if names != nil { | ||||
| 		if namedInt, ok := names[strings.ToLower(expr)]; ok { | ||||
| 			return namedInt | ||||
| 		} | ||||
| 	} | ||||
| 	return mustParseInt(expr) | ||||
| } | ||||
|  | ||||
| // mustParseInt parses the given expression as an int or panics. | ||||
| func mustParseInt(expr string) uint { | ||||
| 	num, err := strconv.Atoi(expr) | ||||
| 	if err != nil { | ||||
| 		log.Panicf("Failed to parse int from %s: %s", expr, err) | ||||
| 	} | ||||
| 	if num < 0 { | ||||
| 		log.Panicf("Negative number (%d) not allowed: %s", num, expr) | ||||
| 	} | ||||
|  | ||||
| 	return uint(num) | ||||
| } | ||||
|  | ||||
| // getBits sets all bits in the range [min, max], modulo the given step size. | ||||
| func getBits(min, max, step uint) uint64 { | ||||
| 	var bits uint64 | ||||
|  | ||||
| 	// If step is 1, use shifts. | ||||
| 	if step == 1 { | ||||
| 		return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min) | ||||
| 	} | ||||
|  | ||||
| 	// Else, use a simple loop. | ||||
| 	for i := min; i <= max; i += step { | ||||
| 		bits |= 1 << i | ||||
| 	} | ||||
| 	return bits | ||||
| } | ||||
|  | ||||
| // all returns all bits within the given bounds.  (plus the star bit) | ||||
| func all(r bounds) uint64 { | ||||
| 	return getBits(r.min, r.max, 1) | starBit | ||||
| } | ||||
|  | ||||
| // parseDescriptor returns a pre-defined schedule for the expression, or panics | ||||
| // if none matches. | ||||
| func parseDescriptor(spec string) Schedule { | ||||
| 	switch spec { | ||||
| 	case "@yearly", "@annually": | ||||
| 		return &SpecSchedule{ | ||||
| 			Second: 1 << seconds.min, | ||||
| 			Minute: 1 << minutes.min, | ||||
| 			Hour:   1 << hours.min, | ||||
| 			Dom:    1 << dom.min, | ||||
| 			Month:  1 << months.min, | ||||
| 			Dow:    all(dow), | ||||
| 		} | ||||
|  | ||||
| 	case "@monthly": | ||||
| 		return &SpecSchedule{ | ||||
| 			Second: 1 << seconds.min, | ||||
| 			Minute: 1 << minutes.min, | ||||
| 			Hour:   1 << hours.min, | ||||
| 			Dom:    1 << dom.min, | ||||
| 			Month:  all(months), | ||||
| 			Dow:    all(dow), | ||||
| 		} | ||||
|  | ||||
| 	case "@weekly": | ||||
| 		return &SpecSchedule{ | ||||
| 			Second: 1 << seconds.min, | ||||
| 			Minute: 1 << minutes.min, | ||||
| 			Hour:   1 << hours.min, | ||||
| 			Dom:    all(dom), | ||||
| 			Month:  all(months), | ||||
| 			Dow:    1 << dow.min, | ||||
| 		} | ||||
|  | ||||
| 	case "@daily", "@midnight": | ||||
| 		return &SpecSchedule{ | ||||
| 			Second: 1 << seconds.min, | ||||
| 			Minute: 1 << minutes.min, | ||||
| 			Hour:   1 << hours.min, | ||||
| 			Dom:    all(dom), | ||||
| 			Month:  all(months), | ||||
| 			Dow:    all(dow), | ||||
| 		} | ||||
|  | ||||
| 	case "@hourly": | ||||
| 		return &SpecSchedule{ | ||||
| 			Second: 1 << seconds.min, | ||||
| 			Minute: 1 << minutes.min, | ||||
| 			Hour:   all(hours), | ||||
| 			Dom:    all(dom), | ||||
| 			Month:  all(months), | ||||
| 			Dow:    all(dow), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	const every = "@every " | ||||
| 	if strings.HasPrefix(spec, every) { | ||||
| 		duration, err := time.ParseDuration(spec[len(every):]) | ||||
| 		if err != nil { | ||||
| 			log.Panicf("Failed to parse duration %s: %s", spec, err) | ||||
| 		} | ||||
| 		return Every(duration) | ||||
| 	} | ||||
|  | ||||
| 	log.Panicf("Unrecognized descriptor: %s", spec) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										159
									
								
								vendor/github.com/gogits/cron/spec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								vendor/github.com/gogits/cron/spec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| package cron | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| // SpecSchedule specifies a duty cycle (to the second granularity), based on a | ||||
| // traditional crontab specification. It is computed initially and stored as bit sets. | ||||
| type SpecSchedule struct { | ||||
| 	Second, Minute, Hour, Dom, Month, Dow uint64 | ||||
| } | ||||
|  | ||||
| // bounds provides a range of acceptable values (plus a map of name to value). | ||||
| type bounds struct { | ||||
| 	min, max uint | ||||
| 	names    map[string]uint | ||||
| } | ||||
|  | ||||
| // The bounds for each field. | ||||
| var ( | ||||
| 	seconds = bounds{0, 59, nil} | ||||
| 	minutes = bounds{0, 59, nil} | ||||
| 	hours   = bounds{0, 23, nil} | ||||
| 	dom     = bounds{1, 31, nil} | ||||
| 	months  = bounds{1, 12, map[string]uint{ | ||||
| 		"jan": 1, | ||||
| 		"feb": 2, | ||||
| 		"mar": 3, | ||||
| 		"apr": 4, | ||||
| 		"may": 5, | ||||
| 		"jun": 6, | ||||
| 		"jul": 7, | ||||
| 		"aug": 8, | ||||
| 		"sep": 9, | ||||
| 		"oct": 10, | ||||
| 		"nov": 11, | ||||
| 		"dec": 12, | ||||
| 	}} | ||||
| 	dow = bounds{0, 6, map[string]uint{ | ||||
| 		"sun": 0, | ||||
| 		"mon": 1, | ||||
| 		"tue": 2, | ||||
| 		"wed": 3, | ||||
| 		"thu": 4, | ||||
| 		"fri": 5, | ||||
| 		"sat": 6, | ||||
| 	}} | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Set the top bit if a star was included in the expression. | ||||
| 	starBit = 1 << 63 | ||||
| ) | ||||
|  | ||||
| // Next returns the next time this schedule is activated, greater than the given | ||||
| // time.  If no time can be found to satisfy the schedule, return the zero time. | ||||
| func (s *SpecSchedule) Next(t time.Time) time.Time { | ||||
| 	// General approach: | ||||
| 	// For Month, Day, Hour, Minute, Second: | ||||
| 	// Check if the time value matches.  If yes, continue to the next field. | ||||
| 	// If the field doesn't match the schedule, then increment the field until it matches. | ||||
| 	// While incrementing the field, a wrap-around brings it back to the beginning | ||||
| 	// of the field list (since it is necessary to re-verify previous field | ||||
| 	// values) | ||||
|  | ||||
| 	// Start at the earliest possible time (the upcoming second). | ||||
| 	t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) | ||||
|  | ||||
| 	// This flag indicates whether a field has been incremented. | ||||
| 	added := false | ||||
|  | ||||
| 	// If no time is found within five years, return zero. | ||||
| 	yearLimit := t.Year() + 5 | ||||
|  | ||||
| WRAP: | ||||
| 	if t.Year() > yearLimit { | ||||
| 		return time.Time{} | ||||
| 	} | ||||
|  | ||||
| 	// Find the first applicable month. | ||||
| 	// If it's this month, then do nothing. | ||||
| 	for 1<<uint(t.Month())&s.Month == 0 { | ||||
| 		// If we have to add a month, reset the other parts to 0. | ||||
| 		if !added { | ||||
| 			added = true | ||||
| 			// Otherwise, set the date at the beginning (since the current time is irrelevant). | ||||
| 			t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) | ||||
| 		} | ||||
| 		t = t.AddDate(0, 1, 0) | ||||
|  | ||||
| 		// Wrapped around. | ||||
| 		if t.Month() == time.January { | ||||
| 			goto WRAP | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Now get a day in that month. | ||||
| 	for !dayMatches(s, t) { | ||||
| 		if !added { | ||||
| 			added = true | ||||
| 			t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | ||||
| 		} | ||||
| 		t = t.AddDate(0, 0, 1) | ||||
|  | ||||
| 		if t.Day() == 1 { | ||||
| 			goto WRAP | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for 1<<uint(t.Hour())&s.Hour == 0 { | ||||
| 		if !added { | ||||
| 			added = true | ||||
| 			t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()) | ||||
| 		} | ||||
| 		t = t.Add(1 * time.Hour) | ||||
|  | ||||
| 		if t.Hour() == 0 { | ||||
| 			goto WRAP | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for 1<<uint(t.Minute())&s.Minute == 0 { | ||||
| 		if !added { | ||||
| 			added = true | ||||
| 			t = t.Truncate(time.Minute) | ||||
| 		} | ||||
| 		t = t.Add(1 * time.Minute) | ||||
|  | ||||
| 		if t.Minute() == 0 { | ||||
| 			goto WRAP | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for 1<<uint(t.Second())&s.Second == 0 { | ||||
| 		if !added { | ||||
| 			added = true | ||||
| 			t = t.Truncate(time.Second) | ||||
| 		} | ||||
| 		t = t.Add(1 * time.Second) | ||||
|  | ||||
| 		if t.Second() == 0 { | ||||
| 			goto WRAP | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // dayMatches returns true if the schedule's day-of-week and day-of-month | ||||
| // restrictions are satisfied by the given time. | ||||
| func dayMatches(s *SpecSchedule, t time.Time) bool { | ||||
| 	var ( | ||||
| 		domMatch bool = 1<<uint(t.Day())&s.Dom > 0 | ||||
| 		dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0 | ||||
| 	) | ||||
|  | ||||
| 	if s.Dom&starBit > 0 || s.Dow&starBit > 0 { | ||||
| 		return domMatch && dowMatch | ||||
| 	} | ||||
| 	return domMatch || dowMatch | ||||
| } | ||||
		Reference in New Issue
	
	Block a user