%PDF- %PDF-
Direktori : /home/waritko/go/src/github.com/odeke-em/drive/src/ |
Current File : //home/waritko/go/src/github.com/odeke-em/drive/src/touch.go |
// Copyright 2015 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package drive import ( "time" ) func multiplexOnChanMapResults(g *Commands, chanMap map[int]chan *keyValue) { spin := g.playabler() spin.play() defer spin.stop() for { if len(chanMap) < 1 { break } // Find the channel that has results for key, kvChan := range chanMap { select { case kv := <-kvChan: if kv == nil { // Sentinel emitted delete(chanMap, key) continue } if kv.value != nil { g.log.LogErrf("touch: %s %v\n", kv.key, kv.value.(error)) } default: } } } return } func (g *Commands) Touch(byId bool) (err error) { // Arbitrary value for rate limiter throttle := time.Tick(1e9 / 10) chanMap := map[int]chan *keyValue{} touchModTime, err := g.requestedTouchModTime() if err != nil { return err } for i, relToRootPath := range g.opts.Sources { fileId := "" if byId { fileId = relToRootPath } chanMap[i] = g.touch(relToRootPath, fileId, g.opts.Depth, touchModTime) <-throttle } multiplexOnChanMapResults(g, chanMap) return } const ( DefaultTouchTimeSpecifier = "20060102150405" ) func (g *Commands) requestedTouchModTime() (*time.Time, error) { var requestedModTime *time.Time metaPtr := g.opts.Meta if metaPtr == nil { return requestedModTime, nil } meta := *metaPtr formatSpecifiers := meta[TouchTimeFmtSpecifierKey] formatSpecifiers = append(formatSpecifiers, DefaultTouchTimeSpecifier) modDateStrL, ok := meta[TouchModTimeKey] if ok && len(modDateStrL) >= 1 { return parseDate(modDateStrL[0], formatSpecifiers...) } // Otherwise for last resort try parsing by duration durationOffsetStrL, ok := meta[TouchOffsetDurationKey] if ok && len(durationOffsetStrL) >= 1 { return parseDurationOffsetFromNow(durationOffsetStrL[0]) } return requestedModTime, nil } func (g *Commands) TouchByMatch() (err error) { mq := matchQuery{ dirPath: g.opts.Path, inTrash: false, titleSearches: []fuzzyStringsValuePair{ {fuzzyLevel: Like, values: g.opts.Sources, inTrash: false}, }, } touchModTime, err := g.requestedTouchModTime() if err != nil { return err } pagePair := g.rem.FindMatches(&mq) errsChan := pagePair.errsChan matchesChan := pagePair.filesChan throttle := time.Tick(1e9 / 10) chanMap := map[int]chan *keyValue{} i := 0 working := true for working { select { case err := <-errsChan: if err != nil { return err } case match, stillHasContent := <-matchesChan: if !stillHasContent { working = false break } if match == nil { continue } chanMap[i] = g.touch(g.opts.Path+"/"+match.Name, match.Id, g.opts.Depth, touchModTime) <-throttle i += 1 } } multiplexOnChanMapResults(g, chanMap) return } // resolveTouch figures out which function to invoke // in order to perform a touch/modTime change of a file. func (g *Commands) resolveTouch(fileId, relToRootPath string, modTime *time.Time) (*File, error) { if fileId == "" { return g.touchByPath(relToRootPath, modTime) } // Now dealing with only fileId if modTime == nil { return g.rem.Touch(fileId) } return g.rem.SetModTime(fileId, *modTime) } func (g *Commands) touch(relToRootPath, fileId string, depth int, modTime *time.Time) chan *keyValue { fileChan := make(chan *keyValue) go func() { kv := &keyValue{ key: relToRootPath, value: nil, } defer func() { fileChan <- kv close(fileChan) }() file, err := g.resolveTouch(fileId, relToRootPath, modTime) if err != nil { kv.value = err return } if g.opts.Verbose { g.log.Logf("%s: %v\n", relToRootPath, file.ModTime) } depth = decrementTraversalDepth(depth) if depth == 0 { return } if file.IsDir { childResults := make(chan chan *keyValue) // Arbitrary value for rate limiter throttle := time.Tick(1e9 * 2) childrenPagePair := g.rem.FindByParentId(file.Id, g.opts.Hidden) errsChan := childrenPagePair.errsChan childrenChan := childrenPagePair.filesChan go func() { defer close(childResults) working := true for working { select { case err := <-errsChan: g.log.LogErrf("%v", err) case child, stillHasContent := <-childrenChan: if !stillHasContent { working = false break } if child == nil { working = false break } childResults <- g.touch(relToRootPath+"/"+child.Name, child.Id, depth, modTime) <-throttle } } }() for childChan := range childResults { for childFile := range childChan { fileChan <- childFile } } } }() return fileChan } func (g *Commands) touchByPath(relToRootPath string, modTime *time.Time) (*File, error) { file, err := g.rem.FindByPath(relToRootPath) if err != nil { return nil, err } if file == nil { return nil, ErrPathNotExists } if modTime == nil { return g.rem.Touch(file.Id) } return g.rem.SetModTime(file.Id, *modTime) }