diff --git a/cmd-diff.go b/cmd-diff.go index 1ee00e2f..d3800f43 100644 --- a/cmd-diff.go +++ b/cmd-diff.go @@ -1,5 +1,5 @@ /* - * Minio Client (C) 2014, 2015 Minio, Inc. + * Minio Client (C) 2015 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,7 @@ package main import ( - "path/filepath" - "github.com/minio/cli" - "github.com/minio/mc/pkg/client" "github.com/minio/mc/pkg/console" "github.com/minio/minio/pkg/iodine" ) @@ -73,141 +70,55 @@ func runDiffCmd(ctx *cli.Context) { } } - doDiffCmd(firstURL, secondURL) + for diff := range doDiffCmd(firstURL, secondURL) { + if diff.err != nil { + console.Fatalf(diff.message) + } + console.Infof(diff.message) + } } -// urlJoinPath - Join a path to existing URL -func urlJoinPath(urlStr string, path string) (newURLStr string, err error) { - u, err := client.Parse(urlStr) +func doDiffInRoutine(firstURL, secondURL string, ch chan diff) { + _, firstContent, err := url2Stat(firstURL) if err != nil { - return "", iodine.New(err, nil) + ch <- diff{ + message: "Failed to stat " + firstURL + ". Reason: [" + iodine.ToError(err).Error() + "]\n", + err: iodine.New(err, nil), + } + return } - - u.Path = filepath.Join(u.Path, path) - newURLStr = u.String() - return newURLStr, nil + _, secondContent, err := url2Stat(secondURL) + if err != nil { + ch <- diff{ + message: "Failed to stat " + secondURL + ". Reason: [" + iodine.ToError(err).Error() + "]\n", + err: iodine.New(err, nil), + } + return + } + if firstContent.Type.IsRegular() { + if !secondContent.Type.IsRegular() { + ch <- diff{ + message: firstURL + " and " + secondURL + " differs in type.\n", + err: nil, + } + } + doDiffObjects(firstURL, secondURL, ch) + } + if firstContent.Type.IsDir() { + if !secondContent.Type.IsDir() { + ch <- diff{ + message: firstURL + " and " + secondURL + " differs in type.\n", + err: nil, + } + } + doDiffDirs(firstURL, secondURL, ch) + } + close(ch) } // doDiffCmd - Execute the diff command -func doDiffCmd(firstURL string, secondURL string) { - _, firstContent, err := URL2Stat(firstURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", firstURL, iodine.ToError(err)) - } - - _, secondContent, err := URL2Stat(secondURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", secondURL, iodine.ToError(err)) - } - - if firstContent.Type.IsRegular() { - if !secondContent.Type.IsRegular() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", firstURL, secondURL) - return - } - doDiffCmdObjects(firstURL, secondURL) - return - } - - if firstContent.Type.IsDir() { - if !secondContent.Type.IsDir() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", firstURL, secondURL) - return - } - doDiffCmdDirs(firstURL, secondURL) - return - } - - console.Fatalf("‘%s’ is of unknown type.\n", firstURL) -} - -// doDiffCmdObjects - Diff two object URLs -func doDiffCmdObjects(firstURL string, secondURL string) { - if firstURL == secondURL { // kind of lame :p - return - } - _, firstContent, err := URL2Stat(firstURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", firstURL, iodine.ToError(err)) - } - - _, secondContent, err := URL2Stat(secondURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", secondURL, iodine.ToError(err)) - } - - if firstContent.Type.IsRegular() { - if !secondContent.Type.IsRegular() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", firstURL, secondURL) - return - } - } else { - console.Fatalf("‘%s’ is not an object. Please report this bug with ‘--debug’ option\n.", firstURL) - } - - if firstContent.Size != secondContent.Size { - console.Infof("‘%s’ and ‘%s’ differs in size.\n", firstURL, secondURL) - } -} - -// doDiffCmdDirs - Diff two Dir URLs -func doDiffCmdDirs(firstURL string, secondURL string) { - firstClnt, firstContent, err := URL2Stat(firstURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", firstURL, iodine.ToError(err)) - } - - _, secondContent, err := URL2Stat(secondURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", secondURL, iodine.ToError(err)) - } - - if firstContent.Type.IsDir() { - if !secondContent.Type.IsDir() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", firstURL, secondURL) - return - } - } else { - console.Fatalf("‘%s’ is not a directory. Please report this bug with ‘--debug’ option\n.", firstURL) - } - - for contentCh := range firstClnt.List() { - if contentCh.Err != nil { - console.Fatalf("Failed to list ‘%s’. Reason: [%s].\n", firstURL, iodine.ToError(contentCh.Err)) - } - - newFirstURL, err := urlJoinPath(firstURL, contentCh.Content.Name) - if err != nil { - console.Fatalf("Unable to construct new URL from ‘%s’ using ‘%s’. Reason: [%s].\n", firstURL, contentCh.Content.Name, iodine.ToError(err)) - } - - newSecondURL, err := urlJoinPath(secondURL, contentCh.Content.Name) - if err != nil { - console.Fatalf("Unable to construct new URL from ‘%s’ using ‘%s’. Reason: [%s].\n", secondURL, contentCh.Content.Name, iodine.ToError(err)) - } - - _, newFirstContent, err := URL2Stat(newFirstURL) - if err != nil { - console.Fatalf("Failed to stat ‘%s’. Reason: [%s].\n", newFirstURL, iodine.ToError(err)) - } - - _, newSecondContent, err := URL2Stat(newSecondURL) - if err != nil { - console.Infof("‘%s’ only in ‘%s’.\n", filepath.Base(newFirstContent.Name), firstURL) - continue - } - - if newFirstContent.Type.IsDir() { - if !newSecondContent.Type.IsDir() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", newFirstURL, newSecondURL) - continue - } - } else if newFirstContent.Type.IsRegular() { - if !newSecondContent.Type.IsRegular() { - console.Infof("‘%s’ and ‘%s’ differs in type.\n", newFirstURL, newSecondURL) - continue - } - doDiffCmdObjects(newFirstURL, newSecondURL) - } - } // End of for-loop +func doDiffCmd(firstURL, secondURL string) <-chan diff { + ch := make(chan diff) + go doDiffInRoutine(firstURL, secondURL, ch) + return ch } diff --git a/common-methods.go b/common-methods.go index c04021a9..5168c180 100644 --- a/common-methods.go +++ b/common-methods.go @@ -75,8 +75,8 @@ func getNewClient(urlStr string, auth *hostConfig, debug bool) (clnt client.Clie return nil, iodine.New(errInvalidURL{url: urlStr}, nil) } -// URL2Stat - Returns client, config and its stat Content from the URL -func URL2Stat(urlStr string) (client client.Client, content *client.Content, err error) { +// url2Stat - Returns client, config and its stat Content from the URL +func url2Stat(urlStr string) (client client.Client, content *client.Content, err error) { config, err := getHostConfig(urlStr) if err != nil { return nil, nil, iodine.New(err, map[string]string{"URL": urlStr}) diff --git a/cp-url.go b/cp-url.go index 5721d491..eeefa775 100644 --- a/cp-url.go +++ b/cp-url.go @@ -421,7 +421,7 @@ func prepareCopyURLs(sourceURLs []string, targetURL string) <-chan *cpURLs { cpURLsCh <- cpURLs } default: - cpURLsCh <- &cpURLs{Error: iodine.New(errors.New("Invalid arguments."), nil)} + cpURLsCh <- &cpURLs{Error: iodine.New(errInvalidArgument{}, nil)} } }(sourceURLs, targetURL, cpURLsCh) diff --git a/diff.go b/diff.go new file mode 100644 index 00000000..2d94c9a2 --- /dev/null +++ b/diff.go @@ -0,0 +1,182 @@ +/* + * Minio Client (C) 2015 Minio, Inc. + * + * 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 main + +import ( + "path/filepath" + + "github.com/minio/mc/pkg/client" + "github.com/minio/minio/pkg/iodine" +) + +type diff struct { + message string + err error +} + +// urlJoinPath Join a path to existing URL +func urlJoinPath(urlStr, path string) (newURLStr string, err error) { + u, err := client.Parse(urlStr) + if err != nil { + return "", iodine.New(err, nil) + } + + u.Path = filepath.Join(u.Path, path) + newURLStr = u.String() + return newURLStr, nil +} + +// doDiffObjects - Diff two object URLs +func doDiffObjects(firstURL, secondURL string, ch chan diff) { + if firstURL == secondURL { + return + } + _, firstContent, err := url2Stat(firstURL) + if err != nil { + ch <- diff{ + message: "Failed to stat ‘" + firstURL + "’ " + "Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + + _, secondContent, err := url2Stat(secondURL) + if err != nil { + ch <- diff{ + message: "Failed to stat ‘" + secondURL + "’ " + "Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + + switch { + case firstContent.Type.IsRegular(): + if !secondContent.Type.IsRegular() { + ch <- diff{ + message: firstURL + " and " + secondURL + " differs in type.\n", + err: nil, + } + } + default: + ch <- diff{ + message: "‘" + firstURL + "’ is not an object. Please report this bug with ‘--debug’ option\n.", + err: iodine.New(errNotAnObject{url: firstURL}, nil), + } + } + + if firstContent.Size != secondContent.Size { + ch <- diff{ + message: firstURL + " and " + secondURL + " differs in size.\n", + err: nil, + } + } +} + +// doDiffDirs - Diff two Dir URLs +func doDiffDirs(firstURL, secondURL string, ch chan diff) { + firstClnt, firstContent, err := url2Stat(firstURL) + if err != nil { + ch <- diff{ + message: "Failed to stat ‘" + firstURL + "’ ." + "Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + _, secondContent, err := url2Stat(secondURL) + if err != nil { + ch <- diff{ + message: "Failed to stat ‘" + secondURL + "’ ." + "Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + switch { + case firstContent.Type.IsDir(): + if !secondContent.Type.IsDir() { + ch <- diff{ + message: firstURL + " and " + secondURL + " differs in type.\n", + err: nil, + } + } + default: + ch <- diff{ + message: "‘" + firstURL + "’ is not an object. Please report this bug with ‘--debug’ option\n.", + err: iodine.New(errNotAnObject{url: firstURL}, nil), + } + } + for contentCh := range firstClnt.List() { + if contentCh.Err != nil { + ch <- diff{ + message: "Failed to list ‘" + firstURL + "’. Reason: [" + iodine.ToError(contentCh.Err).Error() + "].\n", + err: iodine.New(contentCh.Err, nil), + } + return + } + newFirstURL, err := urlJoinPath(firstURL, contentCh.Content.Name) + if err != nil { + ch <- diff{ + message: "Unable to construct new URL from ‘" + firstURL + "’ using ‘" + contentCh.Content.Name + "’. Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + newSecondURL, err := urlJoinPath(secondURL, contentCh.Content.Name) + if err != nil { + ch <- diff{ + message: "Unable to construct new URL from ‘" + secondURL + "’ using ‘" + contentCh.Content.Name + "’. Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + } + _, newFirstContent, err := url2Stat(newFirstURL) + if err != nil { + ch <- diff{ + message: "Failed to stat ‘" + newFirstURL + "’. Reason: [" + iodine.ToError(err).Error() + "].\n", + err: iodine.New(err, nil), + } + return + + } + _, newSecondContent, err := url2Stat(newSecondURL) + if err != nil { + ch <- diff{ + message: "‘" + filepath.Base(newFirstContent.Name) + "’ only in ‘" + firstURL + "’.\n", + err: nil, + } + continue + } + switch { + case newFirstContent.Type.IsDir(): + if !newSecondContent.Type.IsDir() { + ch <- diff{ + message: newFirstURL + " and " + newSecondURL + " differs in type.\n", + err: nil, + } + continue + } + case newFirstContent.Type.IsRegular(): + if !newSecondContent.Type.IsRegular() { + ch <- diff{ + message: newFirstURL + " and " + newSecondURL + " differs in type.\n", + err: nil, + } + continue + } + doDiffObjects(newFirstURL, newSecondURL, ch) + } + } // End of for-loop +} diff --git a/errors.go b/errors.go index 1f31e8e9..134fa2f2 100644 --- a/errors.go +++ b/errors.go @@ -16,6 +16,14 @@ package main +type errNotAnObject struct { + url string +} + +func (e errNotAnObject) Error() string { + return "Not an object " + e.url +} + type errInvalidACL struct { acl string } @@ -27,7 +35,7 @@ func (e errInvalidACL) Error() string { type errInvalidArgument struct{} func (e errInvalidArgument) Error() string { - return "Invalid argument" + return "Invalid argument." } type errUnsupportedScheme struct { diff --git a/sync-url.go b/sync-url.go index ea2bdc69..f62744d4 100644 --- a/sync-url.go +++ b/sync-url.go @@ -16,11 +16,7 @@ package main -import ( - "errors" - - "github.com/minio/minio/pkg/iodine" -) +import "github.com/minio/minio/pkg/iodine" // // NOTE: All the parse rules should reduced to A: Copy(Source, Target). @@ -65,7 +61,7 @@ func prepareSyncURLs(sourceURL string, targetURLs []string) <-chan *cpURLs { syncURLsCh <- syncURLs } default: - syncURLsCh <- &cpURLs{Error: iodine.New(errors.New("Invalid arguments."), nil)} + syncURLsCh <- &cpURLs{Error: iodine.New(errInvalidArgument{}, nil)} } } }()