github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/test/info/internal/internal.go (about) 1 // Package internal provides internal implementation for the info test command. 2 package internal 3 4 import ( 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "strings" 9 ) 10 11 // Presence describes the presence of a filename in file listing 12 type Presence int 13 14 // Possible Presence states 15 const ( 16 Absent Presence = iota 17 Present 18 Renamed 19 Multiple 20 ) 21 22 // Position is the placement of the test character in the filename 23 type Position int 24 25 // Predefined positions 26 const ( 27 PositionMiddle Position = 1 << iota 28 PositionLeft 29 PositionRight 30 PositionNone Position = 0 31 PositionAll Position = PositionRight<<1 - 1 32 ) 33 34 // PositionList contains all valid positions 35 var PositionList = []Position{PositionMiddle, PositionLeft, PositionRight} 36 37 // ControlResult contains the result of a single character test 38 type ControlResult struct { 39 Text string `json:"-"` 40 WriteError map[Position]string 41 GetError map[Position]string 42 InList map[Position]Presence 43 } 44 45 // InfoReport is the structure of the JSON output 46 type InfoReport struct { 47 Remote string 48 ControlCharacters *map[string]ControlResult 49 MaxFileLength *int 50 CanStream *bool 51 CanWriteUnnormalized *bool 52 CanReadUnnormalized *bool 53 CanReadRenormalized *bool 54 } 55 56 func (e Position) String() string { 57 switch e { 58 case PositionNone: 59 return "none" 60 case PositionAll: 61 return "all" 62 } 63 var buf bytes.Buffer 64 if e&PositionMiddle != 0 { 65 buf.WriteString("middle") 66 e &= ^PositionMiddle 67 } 68 if e&PositionLeft != 0 { 69 if buf.Len() != 0 { 70 buf.WriteRune(',') 71 } 72 buf.WriteString("left") 73 e &= ^PositionLeft 74 } 75 if e&PositionRight != 0 { 76 if buf.Len() != 0 { 77 buf.WriteRune(',') 78 } 79 buf.WriteString("right") 80 e &= ^PositionRight 81 } 82 if e != PositionNone { 83 panic("invalid position") 84 } 85 return buf.String() 86 } 87 88 // MarshalText encodes the position when used as a map key 89 func (e Position) MarshalText() ([]byte, error) { 90 return []byte(e.String()), nil 91 } 92 93 // UnmarshalText decodes a position when used as a map key 94 func (e *Position) UnmarshalText(text []byte) error { 95 switch s := strings.ToLower(string(text)); s { 96 default: 97 *e = PositionNone 98 for _, p := range strings.Split(s, ",") { 99 switch p { 100 case "left": 101 *e |= PositionLeft 102 case "middle": 103 *e |= PositionMiddle 104 case "right": 105 *e |= PositionRight 106 default: 107 return fmt.Errorf("unknown position: %s", e) 108 } 109 } 110 case "none": 111 *e = PositionNone 112 case "all": 113 *e = PositionAll 114 } 115 return nil 116 } 117 118 func (e Presence) String() string { 119 switch e { 120 case Absent: 121 return "absent" 122 case Present: 123 return "present" 124 case Renamed: 125 return "renamed" 126 case Multiple: 127 return "multiple" 128 default: 129 panic("invalid presence") 130 } 131 } 132 133 // MarshalJSON encodes the presence when used as a JSON value 134 func (e Presence) MarshalJSON() ([]byte, error) { 135 return json.Marshal(e.String()) 136 } 137 138 // UnmarshalJSON decodes a presence when used as a JSON value 139 func (e *Presence) UnmarshalJSON(text []byte) error { 140 var s string 141 if err := json.Unmarshal(text, &s); err != nil { 142 return err 143 } 144 switch s := strings.ToLower(s); s { 145 case "absent": 146 *e = Absent 147 case "present": 148 *e = Present 149 case "renamed": 150 *e = Renamed 151 case "multiple": 152 *e = Multiple 153 default: 154 return fmt.Errorf("unknown presence: %s", e) 155 } 156 return nil 157 }