Repository: peak/s5cmd (go)

Issue:
Add an option to show storage class
`s5cmd ls` show a custom abbreviation of an objects storage class. It's better to show full name of a class, instead of an abbreviation.

I'm thinking of `-s` to show **s**torage class. For example;

```
$ s5cmd ls -s s3://bucket/

2020/05/06 09:35:18               STANDARD   13 a.xml
2020/05/06 09:33:57             ONEZONE_IA   23 a.html
2020/05/06 09:34:31            STANDARD_IA   14 a.json
2020/05/06 09:35:44    INTELLIGENT_TIERING   15 a.md
```


Pull request description:
command/ls, ese/ls_test: added an option to show storage class
Resolves #165 

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 5519221..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-version: 2.1
-
-jobs:
-  build-unix:
-    docker:
-      - image: circleci/golang:1.14
-
-    working_directory: /go/src/github.com/peak/s5cmd
-
-    steps:
-      - checkout
-      - run: go mod verify
-      - run: go build -mod=vendor .
-
-  test:
-    docker:
-      - image: circleci/golang:1.14
-
-    working_directory: /go/src/github.com/peak/s5cmd
-
-    steps:
-      - checkout
-      - run: make test
-
-  qa:
-    docker:
-      - image: circleci/golang:1.14
-
-    working_directory: /go/src/github.com/peak/s5cmd
-
-    steps:
-      - checkout
-      - run: make check-fmt
-      - run: make vet
-      - run: go get honnef.co/go/tools/cmd/staticcheck
-      - run: go get mvdan.cc/unparam
-      - run: make staticcheck
-      - run: make unparam
-
-workflows:
-  version: 2.1
-  build:
-    jobs:
-      - build-unix
-      - test
-      - qa
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1b2cc86..640b3ea 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,14 +41,7 @@ jobs:
     - run: make test
 
   qa:
-    strategy:
-      matrix:
-        os:
-          - macos
-          - ubuntu
-          - windows
-
-    runs-on: ${{ matrix.os }}-latest
+    runs-on: ubuntu-latest
 
     steps:
     - uses: actions/checkout@v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2f90e2..f1fd703 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 
 ## not released yet
 
+#### Breaking changes
+- Dropped storage class short codes display from default behaviour of `ls` operation. Instead, use `-s` flag with `ls`
+to see full names of the storage classes when listing objects.
+
+#### Features
+- Added an option to show full form of [storage class](https://aws.amazon.com/s3/storage-classes/) when listing objects. ([#165](https://github.com/peak/s5cmd/issues/165))
 
 #### Bugfixes
 
diff --git a/command/ls.go b/command/ls.go
index 8d4c0f9..2a1214d 100644
--- a/command/ls.go
+++ b/command/ls.go
@@ -53,6 +53,11 @@ var listCommand = &cli.Command{
 			Aliases: []string{"H"},
 			Usage:   "human-readable output for object sizes",
 		},
+		&cli.BoolFlag{
+			Name:    "storage-class",
+			Aliases: []string{"s"},
+			Usage:   "display full name of the object class",
+		},
 	},
 	Before: func(c *cli.Context) error {
 		if c.Args().Len() > 1 {
@@ -67,12 +72,14 @@ var listCommand = &cli.Command{
 
 		showEtag := c.Bool("etag")
 		humanize := c.Bool("humanize")
+		showStorageClass := c.Bool("storage-class")
 
 		return List(
 			c.Context,
 			c.Args().First(),
 			showEtag,
 			humanize,
+			showStorageClass,
 		)
 	},
 }
@@ -102,6 +109,7 @@ func List(
 	// flags
 	showEtag bool,
 	humanize bool,
+	showStorageClass bool,
 ) error {
 	srcurl, err := url.New(src)
 	if err != nil {
@@ -123,9 +131,10 @@ func List(
 		}
 
 		msg := ListMessage{
-			Object:        object,
-			showEtag:      showEtag,
-			showHumanized: humanize,
+			Object:           object,
+			showEtag:         showEtag,
+			showHumanized:    humanize,
+			showStorageClass: showStorageClass,
 		}
 
 		log.Info(msg)
@@ -138,8 +147,9 @@ func List(
 type ListMessage struct {
 	Object *storage.Object `json:"object"`
 
-	showEtag      bool
-	showHumanized bool
+	showEtag         bool
+	showHumanized    bool
+	showStorageClass bool
 }
 
 // humanize is a helper function to humanize bytes.
@@ -178,10 +188,15 @@ func (l ListMessage) String() string {
 		return s
 	}
 
+	stclass := ""
+	if l.showStorageClass {
+		stclass = fmt.Sprintf("%v", l.Object.StorageClass)
+	}
+
 	s := fmt.Sprintf(
 		listFormat,
 		l.Object.ModTime.Format(dateFormat),
-		l.Object.StorageClass.ShortCode(),
+		stclass,
 		etag,
 		l.humanize(),
 		l.Object.URL.Relative(),
diff --git a/storage/storage.go b/storage/storage.go
index f8e9845..22aa23e 100644
--- a/storage/storage.go
+++ b/storage/storage.go
@@ -161,30 +161,6 @@ func (b Bucket) JSON() string {
 // StorageClass represents the storage used to store an object.
 type StorageClass string
 
-// ShortCode returns the short code of Amazon S3 Storage Class.
-func (s StorageClass) ShortCode() string {
-	var code string
-	switch s {
-	case "STANDARD":
-		code = "ST"
-	case "REDUCED_REDUNDANCY":
-		code = "RR"
-	case "STANDARD_IA":
-		code = "SI"
-	case "ONEZONE_IA":
-		code = "OI"
-	case "INTELLIGENT_TIERING":
-		code = "IT"
-	case "GLACIER":
-		code = "GL"
-	case "DEEP_ARCHIVE":
-		code = "DA"
-	default:
-		code = " ?"
-	}
-	return code
-}
-
 func (s StorageClass) IsGlacier() bool {
 	return s == "GLACIER"
 }


Test patch:
diff --git a/e2e/cp_test.go b/e2e/cp_test.go
index fe077e2..a7d06a4 100644
--- a/e2e/cp_test.go
+++ b/e2e/cp_test.go
@@ -838,7 +838,6 @@ func TestCopyDirBackslashedToS3(t *testing.T) {
 
 	result.Assert(t, icmd.Success)
 
-
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(`cp %v/readme.md %vreadme.md`, srcpath, dstpath),
 		1: equals(`cp %v/t\est/filetest.txt %vt\est/filetest.txt`, srcpath, dstpath),
diff --git a/e2e/ls_test.go b/e2e/ls_test.go
index 9a34c58..8524df0 100644
--- a/e2e/ls_test.go
+++ b/e2e/ls_test.go
@@ -139,6 +139,26 @@ func TestListSingleWildcardS3Object(t *testing.T) {
 	}, alignment(true))
 }
 
+// ls -s bucket/object
+func TestListS3ObjectsWithDashS(t *testing.T) {
+	t.Parallel()
+
+	bucket := s3BucketFromTestName(t)
+
+	s3client, s5cmd, cleanup := setup(t)
+	defer cleanup()
+
+	createBucket(t, s3client, bucket)
+	putFile(t, s3client, bucket, "testfile1.txt", "this is a file content")
+
+	cmd := s5cmd("ls -s", "s3://"+bucket+"/testfile1.txt")
+	result := icmd.RunCmd(cmd)
+
+	result.Assert(t, icmd.Success)
+
+	// TODO: test if full form of storage class is displayed (it can be done when and if gofakes3 supports storage classes)
+}
+
 // ls bucket/*/object*.ext
 func TestListMultipleWildcardS3Object(t *testing.T) {
 	t.Parallel()
@@ -243,8 +263,8 @@ func TestListS3ObjectsAndFolders(t *testing.T) {
 		3: suffix("DIR d/"),
 		4: suffix("DIR e/"),
 		5: suffix("DIR f/"),
-		6: suffix("? 298 report.gz"),
-		7: suffix("? 302 testfile1.txt"),
+		6: suffix("298 report.gz"),
+		7: suffix("302 testfile1.txt"),
 	}, alignment(true))
 }
 
@@ -271,7 +291,7 @@ func TestListS3ObjectsAndFoldersWithPrefix(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: suffix("DIR t/"),
-		1: suffix("? 302 testfile1.txt"),
+		1: suffix("302 testfile1.txt"),
 	}, alignment(true))
 }
 
@@ -342,8 +362,8 @@ func TestListS3ObjectsWithDashE(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: match(`^ \? \w+ \d+ testfile1.txt$`),
-		1: match(`^ \? \w+ \d+ testfile2.txt$`),
+		0: match(`^ \w+ \d+ testfile1.txt$`),
+		1: match(`^ \w+ \d+ testfile2.txt$`),
 	}, trimMatch(dateRe), alignment(true))
 }
 
@@ -367,7 +387,7 @@ func TestListS3ObjectsWithDashH(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: match(`^ \? 215.1K testfile1.txt$`),
-		1: match(`^ \? 264.0K testfile2.txt$`),
+		0: match(`^ 215.1K testfile1.txt$`),
+		1: match(`^ 264.0K testfile2.txt$`),
 	}, trimMatch(dateRe), alignment(true))
 }