2018-10-15 15:10:20 +01:00
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package fpath
import (
"errors"
"fmt"
2018-12-04 11:42:16 +00:00
"io"
2018-10-15 15:10:20 +01:00
"net/url"
2018-12-03 15:51:56 +00:00
"os"
2018-10-15 15:10:20 +01:00
"path"
"path/filepath"
2018-12-03 15:51:56 +00:00
"runtime"
2018-10-15 15:10:20 +01:00
"strings"
)
2018-10-16 20:50:37 +01:00
// Create a set
var storjScheme = map [ string ] struct { } {
"sj" : { } ,
"s3" : { } ,
}
2018-10-15 15:10:20 +01:00
// FPath is an OS independently path handling structure
type FPath struct {
original string // the original URL or local path
local bool // if local path
bucket string // only for Storj URL
path string // only for Storj URL - the path within the bucket, cleaned from duplicated slashes
}
// New creates new FPath from the given URL
func New ( p string ) ( FPath , error ) {
fp := FPath { original : p }
if filepath . IsAbs ( p ) {
fp . local = true
return fp , nil
}
var u * url . URL
var err error
for {
u , err = url . Parse ( p )
if err != nil {
return fp , fmt . Errorf ( "malformed URL: %v, use format sj://bucket/" , err )
}
if u . Scheme == "" {
fp . local = true
return fp , nil
}
2018-10-16 20:50:37 +01:00
if _ , validScheme := storjScheme [ u . Scheme ] ; ! validScheme {
2018-10-15 15:10:20 +01:00
return fp , fmt . Errorf ( "unsupported URL scheme: %s, use format sj://bucket/" , u . Scheme )
}
if u . Host == "" && u . Path == "" {
return fp , errors . New ( "no bucket specified, use format sj://bucket/" )
}
if u . Host != "" {
break
}
p = strings . Replace ( p , ":///" , "://" , 1 )
}
if u . Port ( ) != "" {
return fp , errors . New ( "port in Storj URL is not supported, use format sj://bucket/" )
}
fp . bucket = u . Host
if u . Path != "" {
fp . path = strings . TrimLeft ( path . Clean ( u . Path ) , "/" )
}
return fp , nil
}
// Join is appends the given segment to the path
func ( p FPath ) Join ( segment string ) FPath {
if p . local {
p . original = filepath . Join ( p . original , segment )
return p
}
p . original += "/" + segment
p . path = path . Join ( p . path , segment )
return p
}
// Base returns the last segment of the path
func ( p FPath ) Base ( ) string {
if p . local {
return filepath . Base ( p . original )
}
if p . path == "" {
return ""
}
return path . Base ( p . path )
}
// Bucket returns the first segment of path
func ( p FPath ) Bucket ( ) string {
return p . bucket
}
// Path returns the URL path without the scheme
func ( p FPath ) Path ( ) string {
if p . local {
return p . original
}
return p . path
}
// IsLocal returns whether the path refers to local or remote location
func ( p FPath ) IsLocal ( ) bool {
return p . local
}
// String returns the entire URL (untouched)
func ( p FPath ) String ( ) string {
return p . original
}
2018-12-03 15:51:56 +00:00
// ApplicationDir returns best base directory for specific OS
func ApplicationDir ( subdir ... string ) string {
for i := range subdir {
if runtime . GOOS == "windows" || runtime . GOOS == "darwin" {
subdir [ i ] = strings . Title ( subdir [ i ] )
} else {
subdir [ i ] = strings . ToLower ( subdir [ i ] )
}
}
var appdir string
home := os . Getenv ( "HOME" )
switch runtime . GOOS {
case "windows" :
// Windows standards: https://msdn.microsoft.com/en-us/library/windows/apps/hh465094.aspx?f=255&MSPPError=-2147217396
for _ , env := range [ ] string { "AppData" , "AppDataLocal" , "UserProfile" , "Home" } {
val := os . Getenv ( env )
if val != "" {
appdir = val
break
}
}
case "darwin" :
// Mac standards: https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html
appdir = filepath . Join ( home , "Library" , "Application Support" )
case "linux" :
fallthrough
default :
// Linux standards: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
appdir = os . Getenv ( "XDG_DATA_HOME" )
if appdir == "" && home != "" {
appdir = filepath . Join ( home , ".local" , "share" )
}
}
return filepath . Join ( append ( [ ] string { appdir } , subdir ... ) ... )
}
2018-12-04 11:42:16 +00:00
// IsValidSetupDir checks if directory is valid for setup configuration
func IsValidSetupDir ( name string ) ( bool , error ) {
_ , err := os . Stat ( name )
if err != nil {
if os . IsNotExist ( err ) {
return true , err
}
return false , err
}
f , err := os . Open ( name )
if err != nil {
return false , err
}
defer func ( ) { _ = f . Close ( ) } ( )
_ , err = f . Readdir ( 1 )
if err == io . EOF {
// is empty
return true , nil
}
return false , err
}