blob: 69337bada8e6c0848d10b9adb022477c7e284514 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"os"
"path/filepath"
"regexp"
"v.io/x/lib/cmdline"
)
var cmdMadbName = &cmdline.Command{
Children: []*cmdline.Command{cmdMadbNameSet, cmdMadbNameUnset, cmdMadbNameList, cmdMadbNameClearAll},
Name: "name",
Short: "Manage device nicknames",
Long: `
Manages device nicknames, which are meant to be more human-friendly compared to
the device serials provided by adb tool.
NOTE: Device specifier flags (-d, -e, -n) are ignored in all 'madb name' commands.
`,
}
var cmdMadbNameSet = &cmdline.Command{
Runner: subCommandRunnerWithFilepath{runMadbNameSet, getDefaultNameFilePath},
Name: "set",
Short: "Set a nickname to be used in place of the device serial.",
Long: `
Sets a human-friendly nickname that can be used when specifying the device in
any madb commands.
The device serial can be obtained using the 'adb devices -l' command.
For example, consider the following example output:
HT4BVWV00023 device usb:3-3.4.2 product:volantisg model:Nexus_9 device:flounder_lte
The first value, 'HT4BVWV00023', is the device serial.
To assign a nickname for this device, run the following command:
madb name set HT4BVWV00023 MyTablet
and it will assign the 'MyTablet' nickname to the device serial 'HT4BVWV00023'.
The alternative device specifiers (e.g., 'usb:3-3.4.2', 'product:volantisg')
can also have nicknames.
When a nickname is set for a device serial, the nickname can be used to specify
the device within madb commands.
There can only be one nickname for a device serial.
When the 'madb name set' command is invoked with a device serial with an already
assigned nickname, the old one will be replaced with the newly provided one.
`,
ArgsName: "<device_serial> <nickname>",
ArgsLong: `
<device_serial> is a device serial (e.g., 'HT4BVWV00023') or an alternative device qualifier (e.g., 'usb:3-3.4.2') obtained from 'adb devices -l' command
<nickname> is an alpha-numeric string with no special characters or spaces.
`,
}
func runMadbNameSet(env *cmdline.Env, args []string, filename string) error {
// Check if the arguments are valid.
if len(args) != 2 {
return env.UsageErrorf("There must be exactly two arguments.")
}
serial, nickname := args[0], args[1]
if !isValidDeviceSerial(serial) {
return env.UsageErrorf("Not a valid device serial: %v", serial)
}
if !isValidNickname(nickname) {
return env.UsageErrorf("Not a valid nickname: %v", nickname)
}
nicknameSerialMap, err := readMapFromFile(filename)
if err != nil {
return err
}
// If the nickname is already in use, don't allow it at all.
if _, present := nicknameSerialMap[nickname]; present {
return fmt.Errorf("The provided nickname %q is already in use.", nickname)
}
// If the serial number already has an assigned nickname, delete it first.
// Need to do this check, because the nickname-serial map should be a one-to-one mapping.
if nickname, present := reverseMap(nicknameSerialMap)[serial]; present {
delete(nicknameSerialMap, nickname)
}
// Add the nickname serial mapping.
nicknameSerialMap[nickname] = serial
return writeMapToFile(nicknameSerialMap, filename)
}
var cmdMadbNameUnset = &cmdline.Command{
Runner: subCommandRunnerWithFilepath{runMadbNameUnset, getDefaultNameFilePath},
Name: "unset",
Short: "Unset a nickname set by the 'madb name set' command.",
Long: `
Unsets a nickname assigned by the 'madb name set' command. Either the device
serial or the assigned nickname can be specified to remove the mapping.
`,
ArgsName: "<device_serial | nickname>",
ArgsLong: `
There should be only one argument, which is either the device serial or the nickname.
`,
}
func runMadbNameUnset(env *cmdline.Env, args []string, filename string) error {
// Check if the arguments are valid.
if len(args) != 1 {
return env.UsageErrorf("There must be exactly one argument.")
}
name := args[0]
if !isValidDeviceSerial(name) && !isValidNickname(name) {
return env.UsageErrorf("Not a valid device serial or name: %v", name)
}
nicknameSerialMap, err := readMapFromFile(filename)
if err != nil {
return err
}
found := false
for nickname, serial := range nicknameSerialMap {
if nickname == name || serial == name {
delete(nicknameSerialMap, nickname)
found = true
break
}
}
if !found {
return fmt.Errorf("The provided argument is neither a known nickname nor a device serial.")
}
return writeMapToFile(nicknameSerialMap, filename)
}
var cmdMadbNameList = &cmdline.Command{
Runner: subCommandRunnerWithFilepath{runMadbNameList, getDefaultNameFilePath},
Name: "list",
Short: "List all the existing nicknames.",
Long: `
Lists all the currently stored nicknames of device serials.
`,
}
func runMadbNameList(env *cmdline.Env, args []string, filename string) error {
nicknameSerialMap, err := readMapFromFile(filename)
if err != nil {
return err
}
// TODO(youngseokyoon): pretty print this.
fmt.Println("Serial Nickname")
fmt.Println("========================")
for nickname, serial := range nicknameSerialMap {
fmt.Printf("%v\t%v\n", serial, nickname)
}
return nil
}
var cmdMadbNameClearAll = &cmdline.Command{
Runner: subCommandRunnerWithFilepath{runMadbNameClearAll, getDefaultNameFilePath},
Name: "clear-all",
Short: "Clear all the existing nicknames.",
Long: `
Clears all the currently stored nicknames of device serials.
`,
}
func runMadbNameClearAll(env *cmdline.Env, args []string, filename string) error {
return os.Remove(filename)
}
func getDefaultNameFilePath() (string, error) {
configDir, err := getConfigDir()
if err != nil {
return "", err
}
return filepath.Join(configDir, "nicknames"), nil
}
func isValidDeviceSerial(serial string) bool {
r := regexp.MustCompile(`^([A-Za-z0-9:\-\._]+|@\d+)$`)
return r.MatchString(serial)
}
func isValidNickname(nickname string) bool {
r := regexp.MustCompile(`^\w+$`)
return r.MatchString(nickname)
}
// reverseMap returns a new map which contains reversed key, value pairs in the original map.
// The source map is assumed to be a one-to-one mapping between keys and values.
func reverseMap(source map[string]string) map[string]string {
if source == nil {
return nil
}
reversed := make(map[string]string, len(source))
for k, v := range source {
reversed[v] = k
}
return reversed
}