diff --git a/Gopkg.lock b/Gopkg.lock
index 90d43ae..7ae85b4 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -41,6 +41,14 @@
pruneopts = "UT"
revision = "05fccaae8fc423476d98fd4c3e4699ba0fbbde48"
+[[projects]]
+ digest = "1:7413525ee648f20b4181be7fe8103d0cb98be9e141926a03ee082dc207061e4e"
+ name = "github.com/phayes/freeport"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "b8543db493a5ed890c5499e935e2cad7504f3a04"
+ version = "1.0.2"
+
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
@@ -52,6 +60,9 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- input-imports = ["github.com/gobuffalo/packr"]
+ input-imports = [
+ "github.com/gobuffalo/packr",
+ "github.com/phayes/freeport",
+ ]
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index f0d94d8..74e1ae0 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -32,3 +32,7 @@
[[constraint]]
name = "github.com/gobuffalo/packr"
version = "1.15.1"
+
+[[constraint]]
+ name = "github.com/phayes/freeport"
+ version = "1.0.2"
diff --git a/README.md b/README.md
index c5dfb1f..7bfb7e6 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,11 @@ npm run package
4. Finish the release: `git flow release finish X.Y.Z`
5. Push to the repository: `git push --all origin && git push --tags`
+## Tasks
+
+- [ ] Auto-complete the directory path
+- [ ] Improve the treemap display
+
## License
DirStat is licensed under the GNU General Public License.
diff --git a/assets/js/dirstat.js b/assets/js/dirstat.js
index 1d50e2b..49e7f3a 100644
--- a/assets/js/dirstat.js
+++ b/assets/js/dirstat.js
@@ -11,6 +11,8 @@ var app = new Vue({
data: {
// Path to the directory to scan
dir: '',
+ // Indicate if the directory stats must also contain files stats
+ includeFiles: 0,
// Scan request currently processing
processing: false,
// Request error
@@ -20,7 +22,7 @@ var app = new Vue({
// Tree map
graph: null,
// Tree map sum mode
- mode: 'count'
+ mode: 'size'
},
// Computed properties
computed: {
@@ -36,6 +38,10 @@ var app = new Vue({
// Available modes
modes: function () {
return ['count', 'size', 'depth'];
+ },
+ // Indicate if the directory path is a root path
+ isRootPath: function () {
+ return this.dir && this.dir.match(/^([A-Z]:)?[\\\/]?[^\\\/]*[\\\/]?$/)
}
},
// Methods
@@ -44,7 +50,7 @@ var app = new Vue({
scan: function () {
this.error = null;
this.processing = true;
- this.$http.get('stat?path=' + this.dir).then(function (data) {
+ this.$http.get('stat?path=' + this.dir + '&files=' + this.includeFiles).then(function (data) {
var entry = data.body;
this.path = [entry];
document.getElementById('treemap-container').innerHTML = '';
@@ -71,9 +77,13 @@ var app = new Vue({
},
// Map entry stats to a string
mapStats: function (d) {
- return 'Size: ' + Math.round(d.size / (1024 * 1024)) + ' MB
'
- + 'Count: ' + d.count + ' entries
'
- + 'Depth: ' + d.depth
+ var desc = 'Type: ' + d.type + '
'
+ + 'Size: ' + Math.round(d.size / (1024 * 1024)) + ' MB
';
+ if (d.type === "Directory") {
+ desc += 'Count: ' + d.count + ' entries
'
+ + 'Depth: ' + d.depth
+ }
+ return desc;
},
// Render the tree map
renderTreemap: function () {
diff --git a/assets/sass/dirstat.scss b/assets/sass/dirstat.scss
index 7ebe979..f896d91 100644
--- a/assets/sass/dirstat.scss
+++ b/assets/sass/dirstat.scss
@@ -1,6 +1,3 @@
-/*
- * dirstat.scss
- */
@import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
[v-cloak] {
@@ -17,3 +14,12 @@ body {
padding-top: 0.4rem !important;
padding-bottom: 0.4rem !important;
}
+
+.alert {
+ &.alert-warning {
+ border-color: darken(#ffeeba, 20%);
+ }
+ &.alert-danger {
+ border-color: darken(#f5c6cb, 15%);
+ }
+}
diff --git a/dirstat.go b/dirstat.go
index c24d5d8..d9ad343 100644
--- a/dirstat.go
+++ b/dirstat.go
@@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"github.com/gobuffalo/packr"
+ "github.com/phayes/freeport"
"io/ioutil"
"log"
"net/http"
@@ -17,11 +18,19 @@ import (
func main() {
// CLI flags
- portFlag := flag.Int("port", 8080, "HTTP server port")
+ portFlag := flag.Int("port", 0, "HTTP server port")
flag.Parse()
// HTTP server address
port := *portFlag
+ if port == 0 {
+ // No port provided: find a free one
+ if freePort, err := freeport.GetFreePort(); err != nil {
+ log.Fatalf("Failed to start DirStat HTTP server: unable to find a free port (%s)", err)
+ } else {
+ port = freePort
+ }
+ }
addr := fmt.Sprintf("127.0.0.1:%d", port)
url := fmt.Sprintf("http://%s", addr)
@@ -62,15 +71,16 @@ func main() {
// Handle GET /stat
// Calculate directory statistics and return them as JSON
func statHandler(res http.ResponseWriter, req *http.Request) {
- // Get path query param
+ // Query parameters
path := req.URL.Query().Get("path")
+ includeFiles := req.URL.Query().Get("files") == "1"
if path == "" {
sendError(res, http.StatusBadRequest, "The path can't be empty")
return
} // else
// Calculate statistics for the given directory path
log.Printf("Scanning directory '%s'...", path)
- if stat, err := scanDir(path); err != nil { // Scan failed
+ if stat, err := scanDir(path, includeFiles); err != nil { // Scan failed
sendError(res, http.StatusBadRequest, "Failed scanning directory ("+err.Error()+")")
} else if output, err := json.Marshal(stat); err != nil { // Marshalling failed
sendError(res, http.StatusInternalServerError, "Failed encoding statistics to JSON ("+err.Error()+")")
@@ -107,33 +117,40 @@ type ErrorMessage struct {
//
// Directory statistics structure.
-type DirStat struct {
- Path string `json:"path"`
- Name string `json:"name"`
- Count int `json:"count"`
- Size int64 `json:"size"`
- Depth int `json:"depth"`
- Children []DirStat `json:"children"`
+type EntryStat struct {
+ Path string `json:"path"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Count int `json:"count"`
+ Size int64 `json:"size"`
+ Depth int `json:"depth"`
+ Children []EntryStat `json:"children"`
}
-// Create a new directory statistics object
+// Create a new entry statistics object for a directory
// from a directory path and set default values.
-func NewDirStat(path string) *DirStat {
- return &DirStat{Path: path, Name: fp.Base(path), Count: 0, Size: 0,
- Depth: 0, Children: []DirStat{}}
+func NewDirectoryStat(path string) *EntryStat {
+ return &EntryStat{Path: path, Name: fp.Base(path), Type: "Directory",
+ Count: 0, Size: 0, Depth: 0, Children: []EntryStat{}}
+}
+
+// Create a new entry statistics object for a file.
+func NewFileStat(path string, name string, size int64) *EntryStat {
+ return &EntryStat{Name: name, Path: path, Type: "File", Size: size,
+ Count: 0, Depth: 0, Children: []EntryStat{}}
}
// Append a child directory statistics object
// to the current directory statistics.
-func (stat *DirStat) append(childStat DirStat) {
+func (stat *EntryStat) append(childStat EntryStat) {
stat.Children = append(stat.Children, childStat)
stat.Size += childStat.Size
stat.Count += childStat.Count
}
// Calculate directory statistics recursively.
-func scanDir(path string) (stat *DirStat, err error) {
- stat = NewDirStat(path)
+func scanDir(path string, includeFiles bool) (stat *EntryStat, err error) {
+ stat = NewDirectoryStat(path)
files, err := ioutil.ReadDir(path)
if err != nil {
msg := fmt.Sprintf("Failed reading directory '%s'", path)
@@ -146,13 +163,18 @@ func scanDir(path string) (stat *DirStat, err error) {
childPath := fp.Join(path, file.Name())
if file.IsDir() {
// Recursive call for directories
- if childStat, err := scanDir(childPath); err == nil {
+ if childStat, err := scanDir(childPath, includeFiles); err == nil {
// Error is ignored
stat.append(*childStat)
}
} else if file.Mode().IsRegular() {
- // Append file size
- stat.Size += file.Size()
+ if includeFiles {
+ // Append whole file stats
+ stat.append(*NewFileStat(childPath, file.Name(), file.Size()))
+ } else {
+ // Only append file size
+ stat.Size += file.Size()
+ }
}
}
// Compute directory depth
diff --git a/package.json b/package.json
index eebe898..452ce9c 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "dir-stat",
- "version": "0.2.0",
+ "version": "0.3.0",
"description": "Simple directory statistics",
"scripts": {
"build:assets": "gulp",
"build": "npm run build:assets && packr build -i -o dirstat.exe .",
"ensure": "npm install && dep ensure",
- "start": "dirstat",
+ "start": "npm run build && dirstat",
"package": "powershell.exe ./scripts/package.ps1"
},
"repository": {
diff --git a/web/index.html b/web/index.html
index d0fc61c..a63300b 100644
--- a/web/index.html
+++ b/web/index.html
@@ -20,18 +20,34 @@