diff options
| author | Petter Rasmussen | 2016-01-17 21:13:46 +0100 | 
|---|---|---|
| committer | Petter Rasmussen | 2016-01-17 21:13:46 +0100 | 
| commit | f35fd0892688ff638b30dcd48fdd56b9e2627cf1 (patch) | |
| tree | 18439804fdb5c87e4ba0f4cea4561db35bdd7868 /drive | |
| parent | f16b89b6f6bee6023c51b4f8120a3e4776128384 (diff) | |
| download | gdrive-f35fd0892688ff638b30dcd48fdd56b9e2627cf1.tar.bz2 | |
One file per command
Diffstat (limited to 'drive')
| -rw-r--r-- | drive/download.go | 55 | ||||
| -rw-r--r-- | drive/drive.go | 23 | ||||
| -rw-r--r-- | drive/files.go | 139 | ||||
| -rw-r--r-- | drive/info.go | 49 | ||||
| -rw-r--r-- | drive/list.go | 55 | ||||
| -rw-r--r-- | drive/mkdir.go | 32 | ||||
| -rw-r--r-- | drive/print.go | 139 | ||||
| -rw-r--r-- | drive/share.go (renamed from drive/permissions.go) | 8 | ||||
| -rw-r--r-- | drive/types.go | 90 | ||||
| -rw-r--r-- | drive/upload.go | 62 | ||||
| -rw-r--r-- | drive/util.go | 100 | 
11 files changed, 383 insertions, 369 deletions
| diff --git a/drive/download.go b/drive/download.go new file mode 100644 index 0000000..9a35912 --- /dev/null +++ b/drive/download.go @@ -0,0 +1,55 @@ +package drive + +import ( +    "fmt" +    "io" +    "os" +) + +type DownloadFileArgs struct { +    Id string +    Force bool +    NoProgress bool +    Stdout bool +} + +func (self *Drive) Download(args DownloadFileArgs) { +    getFile := self.service.Files.Get(args.Id) + +    f, err := getFile.Do() +    errorF(err, "Failed to get file: %s", err) + +    res, err := getFile.Download() +    errorF(err, "Failed to download file: %s", err) + +    // Close body on function exit +    defer res.Body.Close() + +    if args.Stdout { +        // Write file content to stdout +        io.Copy(os.Stdout, res.Body) +        return +    } + +    // Check if file exists +    if !args.Force && fileExists(f.Name) { +        exitF("File '%s' already exists, use --force to overwrite", f.Name) +    } + +    // Create new file +    outFile, err := os.Create(f.Name) +    errorF(err, "Unable to create new file: %s", err) + +    // Close file on function exit +    defer outFile.Close() + +    // Save file to disk +    bytes, err := io.Copy(outFile, res.Body) +    errorF(err, "Failed saving file: %s", err) + +    fmt.Printf("Downloaded '%s' at %s, total %d\n", f.Name, "x/s", bytes) + +    //if deleteSourceFile { +    //    self.Delete(args.Id) +    //} +} diff --git a/drive/drive.go b/drive/drive.go new file mode 100644 index 0000000..047030a --- /dev/null +++ b/drive/drive.go @@ -0,0 +1,23 @@ +package drive + +import ( +    "net/http" +    "google.golang.org/api/drive/v3" +) + +type Client interface { +    Service() *drive.Service +    Http() *http.Client +} + +type Drive struct { +    service *drive.Service +    http *http.Client +} + +func NewDrive(client Client) *Drive { +    return &Drive{ +        service: client.Service(), +        http: client.Http(), +    } +} diff --git a/drive/files.go b/drive/files.go deleted file mode 100644 index b6eed04..0000000 --- a/drive/files.go +++ /dev/null @@ -1,139 +0,0 @@ -package drive - -import ( -    "fmt" -    "io" -    "mime" -    "os" -    "path/filepath" -    "google.golang.org/api/drive/v3" -    "golang.org/x/net/context" -) - -const DirectoryMimeType = "application/vnd.google-apps.folder" - - -func (self *Drive) List(args ListFilesArgs) { -    fileList, err := self.service.Files.List().PageSize(args.MaxFiles).Q(args.Query).Fields("nextPageToken", "files(id,name,size,createdTime)").Do() -    errorF(err, "Failed listing files: %s\n", err) - -    PrintFileList(PrintFileListArgs{ -        Files: fileList.Files, -        NameWidth: int(args.NameWidth), -        SkipHeader: args.SkipHeader, -        SizeInBytes: args.SizeInBytes, -    }) -} - - -func (self *Drive) Download(args DownloadFileArgs) { -    getFile := self.service.Files.Get(args.Id) - -    f, err := getFile.Do() -    errorF(err, "Failed to get file: %s", err) - -    res, err := getFile.Download() -    errorF(err, "Failed to download file: %s", err) - -    // Close body on function exit -    defer res.Body.Close() - -    if args.Stdout { -        // Write file content to stdout -        io.Copy(os.Stdout, res.Body) -        return -    } - -    // Check if file exists -    if !args.Force && fileExists(f.Name) { -        exitF("File '%s' already exists, use --force to overwrite", f.Name) -    } - -    // Create new file -    outFile, err := os.Create(f.Name) -    errorF(err, "Unable to create new file: %s", err) - -    // Close file on function exit -    defer outFile.Close() - -    // Save file to disk -    bytes, err := io.Copy(outFile, res.Body) -    errorF(err, "Failed saving file: %s", err) - -    fmt.Printf("Downloaded '%s' at %s, total %d\n", f.Name, "x/s", bytes) - -    //if deleteSourceFile { -    //    self.Delete(args.Id) -    //} -} - -func (self *Drive) Upload(args UploadFileArgs) { -    //if args.Stdin { -    //    self.uploadStdin() -    //} - -    srcFile, err := os.Open(args.Path) -    errorF(err, "Failed to open file: %s", err) - -    srcFileInfo, err := srcFile.Stat() -    errorF(err, "Failed to read file metadata: %s", err) - -    // Instantiate empty drive file -    dstFile := &drive.File{} - -    // Use provided file name or use filename -    if args.Name == "" { -        dstFile.Name = filepath.Base(srcFileInfo.Name()) -    } else { -        dstFile.Name = args.Name -    } - -    // Set provided mime type or get type based on file extension -    if args.Mime == "" { -        dstFile.MimeType = mime.TypeByExtension(filepath.Ext(dstFile.Name)) -    } else { -        dstFile.MimeType = args.Mime -    } - -    // Set parent folder if provided -    if args.Parent != "" { -        dstFile.Parents = []string{args.Parent} -    } - -    f, err := self.service.Files.Create(dstFile).ResumableMedia(context.Background(), srcFile, srcFileInfo.Size(), dstFile.MimeType).Do() -    errorF(err, "Failed to upload file: %s", err) - -    fmt.Printf("Uploaded '%s' at %s, total %d\n", f.Name, "x/s", f.Size) -    //if args.Share { -    //    self.Share(TODO) -    //} -} - -func (self *Drive) Info(args FileInfoArgs) { -    f, err := self.service.Files.Get(args.Id).Fields("id", "name", "size", "createdTime", "modifiedTime", "md5Checksum", "mimeType", "parents", "shared", "description").Do() -    errorF(err, "Failed to get file: %s", err) - -    PrintFileInfo(PrintFileInfoArgs{ -        File: f, -        SizeInBytes: args.SizeInBytes, -    }) -} - -func (self *Drive) Mkdir(args MkdirArgs) { -    dstFile := &drive.File{Name: args.Name, MimeType: DirectoryMimeType} - -    // Set parent folder if provided -    if args.Parent != "" { -        dstFile.Parents = []string{args.Parent} -    } - -    // Create folder -    f, err := self.service.Files.Create(dstFile).Do() -    errorF(err, "Failed to create folder: %s", err) - -    PrintFileInfo(PrintFileInfoArgs{File: f}) - -    //if args.Share { -    //    self.Share(TODO) -    //} -} diff --git a/drive/info.go b/drive/info.go new file mode 100644 index 0000000..f5f5602 --- /dev/null +++ b/drive/info.go @@ -0,0 +1,49 @@ +package drive + +import ( +    "fmt" +    "google.golang.org/api/drive/v3" +) + +type FileInfoArgs struct { +    Id string +    SizeInBytes bool +} + +func (self *Drive) Info(args FileInfoArgs) { +    f, err := self.service.Files.Get(args.Id).Fields("id", "name", "size", "createdTime", "modifiedTime", "md5Checksum", "mimeType", "parents", "shared", "description").Do() +    errorF(err, "Failed to get file: %s", err) + +    PrintFileInfo(PrintFileInfoArgs{ +        File: f, +        SizeInBytes: args.SizeInBytes, +    }) +} + +type PrintFileInfoArgs struct { +    File *drive.File +    SizeInBytes bool +} + +func PrintFileInfo(args PrintFileInfoArgs) { +    f := args.File + +    items := []kv{ +        kv{"Id", f.Id}, +        kv{"Name", f.Name}, +        kv{"Description", f.Description}, +        kv{"Mime", f.MimeType}, +        kv{"Size", formatSize(f.Size, args.SizeInBytes)}, +        kv{"Created", formatDatetime(f.CreatedTime)}, +        kv{"Modified", formatDatetime(f.ModifiedTime)}, +        kv{"Md5sum", f.Md5Checksum}, +        kv{"Shared", formatBool(f.Shared)}, +        kv{"Parents", formatList(f.Parents)}, +    } + +    for _, item := range items { +        if item.value() != "" { +            fmt.Printf("%s: %s\n", item.key(), item.value()) +        } +    } +} diff --git a/drive/list.go b/drive/list.go new file mode 100644 index 0000000..c542785 --- /dev/null +++ b/drive/list.go @@ -0,0 +1,55 @@ +package drive + +import ( +    "fmt" +    "os" +    "text/tabwriter" +    "google.golang.org/api/drive/v3" +) + +type ListFilesArgs struct { +    MaxFiles int64 +    NameWidth int64 +    Query string +    SkipHeader bool +    SizeInBytes bool +} + +func (self *Drive) List(args ListFilesArgs) { +    fileList, err := self.service.Files.List().PageSize(args.MaxFiles).Q(args.Query).Fields("nextPageToken", "files(id,name,size,createdTime)").Do() +    errorF(err, "Failed listing files: %s\n", err) + +    PrintFileList(PrintFileListArgs{ +        Files: fileList.Files, +        NameWidth: int(args.NameWidth), +        SkipHeader: args.SkipHeader, +        SizeInBytes: args.SizeInBytes, +    }) +} + +type PrintFileListArgs struct { +    Files []*drive.File +    NameWidth int +    SkipHeader bool +    SizeInBytes bool +} + +func PrintFileList(args PrintFileListArgs) { +    w := new(tabwriter.Writer) +    w.Init(os.Stdout, 0, 0, 3, ' ', 0) + +    if !args.SkipHeader { +        fmt.Fprintln(w, "Id\tName\tSize\tCreated") +    } + +    for _, f := range args.Files { +        fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", +            f.Id, +            truncateString(f.Name, args.NameWidth), +            formatSize(f.Size, args.SizeInBytes), +            formatDatetime(f.CreatedTime), +        ) +    } + +    w.Flush() +} diff --git a/drive/mkdir.go b/drive/mkdir.go new file mode 100644 index 0000000..8295649 --- /dev/null +++ b/drive/mkdir.go @@ -0,0 +1,32 @@ +package drive + +import ( +    "google.golang.org/api/drive/v3" +) + +const DirectoryMimeType = "application/vnd.google-apps.folder" + +type MkdirArgs struct { +    Name string +    Parent string +    Share bool +} + +func (self *Drive) Mkdir(args MkdirArgs) { +    dstFile := &drive.File{Name: args.Name, MimeType: DirectoryMimeType} + +    // Set parent folder if provided +    if args.Parent != "" { +        dstFile.Parents = []string{args.Parent} +    } + +    // Create folder +    f, err := self.service.Files.Create(dstFile).Do() +    errorF(err, "Failed to create folder: %s", err) + +    PrintFileInfo(PrintFileInfoArgs{File: f}) + +    //if args.Share { +    //    self.Share(TODO) +    //} +} diff --git a/drive/print.go b/drive/print.go deleted file mode 100644 index 84d4ec7..0000000 --- a/drive/print.go +++ /dev/null @@ -1,139 +0,0 @@ -package drive - -import ( -    "os" -    "fmt" -    "text/tabwriter" -    "strings" -    "strconv" -    "unicode/utf8" -    "time" -) - - -func PrintFileList(args PrintFileListArgs) { -    w := new(tabwriter.Writer) -    w.Init(os.Stdout, 0, 0, 3, ' ', 0) - -    if !args.SkipHeader { -        fmt.Fprintln(w, "Id\tName\tSize\tCreated") -    } - -    for _, f := range args.Files { -        fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", -            f.Id, -            truncateString(f.Name, args.NameWidth), -            formatSize(f.Size, args.SizeInBytes), -            formatDatetime(f.CreatedTime), -        ) -    } - -    w.Flush() -} - -func PrintFileInfo(args PrintFileInfoArgs) { -    f := args.File - -    items := []kv{ -        kv{"Id", f.Id}, -        kv{"Name", f.Name}, -        kv{"Description", f.Description}, -        kv{"Mime", f.MimeType}, -        kv{"Size", formatSize(f.Size, args.SizeInBytes)}, -        kv{"Created", formatDatetime(f.CreatedTime)}, -        kv{"Modified", formatDatetime(f.ModifiedTime)}, -        kv{"Md5sum", f.Md5Checksum}, -        kv{"Shared", formatBool(f.Shared)}, -        kv{"Parents", formatList(f.Parents)}, -    } - -    for _, item := range items { -        if item.value() != "" { -            fmt.Printf("%s: %s\n", item.key(), item.value()) -        } -    } -} - -func formatList(a []string) string { -    return strings.Join(a, ", ") -} - -func formatSize(bytes int64, forceBytes bool) string { -    if bytes == 0 { -        return "" -    } - -    if forceBytes { -        return fmt.Sprintf("%v B", bytes) -    } - -    units := []string{"B", "KB", "MB", "GB", "TB", "PB"} - -    var i int -    value := float64(bytes) - -    for value > 1000 { -        value /= 1000 -        i++ -    } -    return fmt.Sprintf("%.1f %s", value, units[i]) -} - -func formatBool(b bool) string { -    return strings.Title(strconv.FormatBool(b)) -} - -func formatDatetime(iso string) string { -    t, err := time.Parse(time.RFC3339, iso) -    if err != nil { -        return iso -    } -    local := t.Local() -    year, month, day := local.Date() -    hour, min, sec := local.Clock() -    return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec) -} - -// Truncates string to given max length, and inserts ellipsis into -// the middle of the string to signify that the string has been truncated -func truncateString(str string, maxRunes int) string { -    indicator := "..." - -    // Number of runes in string -    runeCount := utf8.RuneCountInString(str) - -    // Return input string if length of input string is less than max length -    // Input string is also returned if max length is less than 9 which is the minmal supported length -    if runeCount <= maxRunes || maxRunes < 9 { -        return str -    } - -    // Number of remaining runes to be removed -    remaining := (runeCount - maxRunes) + utf8.RuneCountInString(indicator) - -    var truncated string -    var skip bool - -    for leftOffset, char := range str { -        rightOffset := runeCount - (leftOffset + remaining) - -        // Start skipping chars when the left and right offsets are equal -        // Or in the case where we wont be able to do an even split: when the left offset is larger than the right offset -        if leftOffset == rightOffset || (leftOffset > rightOffset && !skip) { -            skip = true -            truncated += indicator -        } - -        if skip && remaining > 0 { -            // Skip char and decrement the remaining skip counter -            remaining-- -            continue -        } - -        // Add char to result string -        truncated += string(char) -    } - -    // Return truncated string -    return truncated -} diff --git a/drive/permissions.go b/drive/share.go index 1e2c004..7e7036d 100644 --- a/drive/permissions.go +++ b/drive/share.go @@ -5,6 +5,14 @@ import (      "google.golang.org/api/drive/v3"  ) +type ShareArgs struct { +    FileId string +    Role string +    Type string +    Email string +    Discoverable bool +    Revoke bool +}  func (self *Drive) Share(args ShareArgs) {      if args.Revoke { diff --git a/drive/types.go b/drive/types.go deleted file mode 100644 index f252dde..0000000 --- a/drive/types.go +++ /dev/null @@ -1,90 +0,0 @@ -package drive - -import ( -    "net/http" -    "google.golang.org/api/drive/v3" -) - -type Client interface { -    Service() *drive.Service -    Http() *http.Client -} - -type Drive struct { -    service *drive.Service -    http *http.Client -} - -func NewDrive(client Client) *Drive { -    return &Drive{ -        service: client.Service(), -        http: client.Http(), -    } -} - -type ListFilesArgs struct { -    MaxFiles int64 -    NameWidth int64 -    Query string -    SkipHeader bool -    SizeInBytes bool -} - -type DownloadFileArgs struct { -    Id string -    Force bool -    NoProgress bool -    Stdout bool -} - -type UploadFileArgs struct { -    Path string -    Name string -    Parent string -    Mime string -    Recursive bool -    Stdin bool -    Share bool -} - -type FileInfoArgs struct { -    Id string -    SizeInBytes bool -} - -type MkdirArgs struct { -    Name string -    Parent string -    Share bool -} - -type ShareArgs struct { -    FileId string -    Role string -    Type string -    Email string -    Discoverable bool -    Revoke bool -} - -type PrintFileListArgs struct { -    Files []*drive.File -    NameWidth int -    SkipHeader bool -    SizeInBytes bool -} - -type PrintFileInfoArgs struct { -    File *drive.File -    SizeInBytes bool -} - -type kv [2]string - -func (self kv) key() string { -    return self[0] -} - -func (self kv) value() string { -    return self[1] -} diff --git a/drive/upload.go b/drive/upload.go new file mode 100644 index 0000000..ed373ef --- /dev/null +++ b/drive/upload.go @@ -0,0 +1,62 @@ +package drive + +import ( +    "fmt" +    "mime" +    "os" +    "path/filepath" +    "google.golang.org/api/drive/v3" +    "golang.org/x/net/context" +) + +type UploadFileArgs struct { +    Path string +    Name string +    Parent string +    Mime string +    Recursive bool +    Stdin bool +    Share bool +} + +func (self *Drive) Upload(args UploadFileArgs) { +    //if args.Stdin { +    //    self.uploadStdin() +    //} + +    srcFile, err := os.Open(args.Path) +    errorF(err, "Failed to open file: %s", err) + +    srcFileInfo, err := srcFile.Stat() +    errorF(err, "Failed to read file metadata: %s", err) + +    // Instantiate empty drive file +    dstFile := &drive.File{} + +    // Use provided file name or use filename +    if args.Name == "" { +        dstFile.Name = filepath.Base(srcFileInfo.Name()) +    } else { +        dstFile.Name = args.Name +    } + +    // Set provided mime type or get type based on file extension +    if args.Mime == "" { +        dstFile.MimeType = mime.TypeByExtension(filepath.Ext(dstFile.Name)) +    } else { +        dstFile.MimeType = args.Mime +    } + +    // Set parent folder if provided +    if args.Parent != "" { +        dstFile.Parents = []string{args.Parent} +    } + +    f, err := self.service.Files.Create(dstFile).ResumableMedia(context.Background(), srcFile, srcFileInfo.Size(), dstFile.MimeType).Do() +    errorF(err, "Failed to upload file: %s", err) + +    fmt.Printf("Uploaded '%s' at %s, total %d\n", f.Name, "x/s", f.Size) +    //if args.Share { +    //    self.Share(TODO) +    //} +} diff --git a/drive/util.go b/drive/util.go index 8dda213..bc2d5c3 100644 --- a/drive/util.go +++ b/drive/util.go @@ -1,10 +1,108 @@  package drive  import ( -    "fmt"      "os" +    "fmt" +    "strings" +    "strconv" +    "unicode/utf8" +    "time"  ) +type kv [2]string + +func (self kv) key() string { +    return self[0] +} + +func (self kv) value() string { +    return self[1] +} + +func formatList(a []string) string { +    return strings.Join(a, ", ") +} + +func formatSize(bytes int64, forceBytes bool) string { +    if bytes == 0 { +        return "" +    } + +    if forceBytes { +        return fmt.Sprintf("%v B", bytes) +    } + +    units := []string{"B", "KB", "MB", "GB", "TB", "PB"} + +    var i int +    value := float64(bytes) + +    for value > 1000 { +        value /= 1000 +        i++ +    } +    return fmt.Sprintf("%.1f %s", value, units[i]) +} + +func formatBool(b bool) string { +    return strings.Title(strconv.FormatBool(b)) +} + +func formatDatetime(iso string) string { +    t, err := time.Parse(time.RFC3339, iso) +    if err != nil { +        return iso +    } +    local := t.Local() +    year, month, day := local.Date() +    hour, min, sec := local.Clock() +    return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec) +} + +// Truncates string to given max length, and inserts ellipsis into +// the middle of the string to signify that the string has been truncated +func truncateString(str string, maxRunes int) string { +    indicator := "..." + +    // Number of runes in string +    runeCount := utf8.RuneCountInString(str) + +    // Return input string if length of input string is less than max length +    // Input string is also returned if max length is less than 9 which is the minmal supported length +    if runeCount <= maxRunes || maxRunes < 9 { +        return str +    } + +    // Number of remaining runes to be removed +    remaining := (runeCount - maxRunes) + utf8.RuneCountInString(indicator) + +    var truncated string +    var skip bool + +    for leftOffset, char := range str { +        rightOffset := runeCount - (leftOffset + remaining) + +        // Start skipping chars when the left and right offsets are equal +        // Or in the case where we wont be able to do an even split: when the left offset is larger than the right offset +        if leftOffset == rightOffset || (leftOffset > rightOffset && !skip) { +            skip = true +            truncated += indicator +        } + +        if skip && remaining > 0 { +            // Skip char and decrement the remaining skip counter +            remaining-- +            continue +        } + +        // Add char to result string +        truncated += string(char) +    } + +    // Return truncated string +    return truncated +} +  func errorF(err error, format string, a ...interface{}) {      if err == nil {          return | 
