func (grp *Group) Attribute(attrName string) (Metas, error) {
	if meta, err := grp.Meta(); err == nil {
		return meta.Get(attrName)
	} else {
		return nil, err
	}
}
Attribute gets slice of Meta AVU triples, matching by Attribute name for Group
package gorods
import "C"
import (
	"fmt"
	"strconv"
	"strings"
	"time"
	"unsafe"
)
type Group struct {
	name        string
	createTime  time.Time
	modifyTime  time.Time
	id          int
	typ         int
	zone        *Zone
	info        string
	comment     string
	parentSlice *Groups
	hasInit bool
	users Users
	con   *Connection
	metaCol *MetaCollection
}
type Groups []*Group
func initGroup(name string, con *Connection) (*Group, error) {
	grp := new(Group)
	grp.name = name
	grp.con = con
	grp.typ = GroupType
	if z, err := con.LocalZone(); err != nil {
		return nil, err
	} else {
		grp.zone = z
	}
	return grp, nil
}
func (grp *Group) Name() string {
	return grp.name
}
func (grp *Group) Path() string {
	return grp.name
}
func (grp *Group) Zone() *Zone {
	return grp.zone
}
func (grp *Group) Comment() (string, error) {
	if err := grp.init(); err != nil {
		return grp.comment, err
	}
	return grp.comment, nil
}
func (grp *Group) CreateTime() (time.Time, error) {
	if err := grp.init(); err != nil {
		return grp.createTime, err
	}
	return grp.createTime, nil
}
func (grp *Group) ModifyTime() (time.Time, error) {
	if err := grp.init(); err != nil {
		return grp.modifyTime, err
	}
	return grp.modifyTime, nil
}
func (grp *Group) Id() (int, error) {
	if err := grp.init(); err != nil {
		return grp.id, err
	}
	return grp.id, nil
}
func (grp *Group) Info() (string, error) {
	if err := grp.init(); err != nil {
		return grp.info, err
	}
	return grp.info, nil
}
func (grp *Group) Type() int {
	return grp.typ
}
func (grp *Group) Con() *Connection {
	return grp.con
}
func (grp *Group) Users() (Users, error) {
	if err := grp.init(); err != nil {
		return nil, err
	}
	return grp.users, nil
}
func (grp *Group) Delete() error {
	if err := deleteGroup(grp.Name(), grp.Zone(), grp.con); err != nil {
		return err
	}
	if err := grp.con.RefreshGroups(); err != nil {
		return err
	}
	return nil
}
func (grp *Group) init() error {
	if !grp.hasInit {
		if err := grp.RefreshInfo(); err != nil {
			return err
		}
		if err := grp.RefreshUsers(); err != nil {
			return err
		}
		grp.hasInit = true
	}
	return nil
}
func (grps Groups) FindByName(name string, con *Connection) *Group {
	for _, grp := range grps {
		if grp.name == name {
			return grp
		}
	}
	grp, _ := initGroup(name, con)
	return grp
}
func (grps *Groups) Remove(index int) {
	*grps = append((*grps)[:index], (*grps)[index+1:]...)
}
func (grp *Group) Remove() bool {
	for n, p := range *grp.parentSlice {
		if p.name == grp.name {
			grp.parentSlice.Remove(n)
			return true
		}
	}
	return false
}
func (grp *Group) String() string {
	return fmt.Sprintf("%v", grp.name)
}
func (grp *Group) RefreshInfo() error {
	if infoMap, err := grp.FetchInfo(); err == nil {
		grp.comment = infoMap["r_comment"]
		grp.createTime = timeStringToTime(infoMap["create_ts"])
		grp.modifyTime = timeStringToTime(infoMap["modify_ts"])
		grp.id, _ = strconv.Atoi(infoMap["user_id"])
		grp.info = infoMap["user_info"]
		if zones, err := grp.con.Zones(); err != nil {
			return err
		} else {
			if zne := zones.FindByName(infoMap["zone_name"], grp.con); zne != nil {
				grp.zone = zne
			} else {
				return newError(Fatal, -1, fmt.Sprintf("iRODS Refresh Group Info Failed: Unable to locate zone in cache"))
			}
		}
	} else {
		return err
	}
	return nil
}
func (grp *Group) RefreshUsers() error {
	if usrs, err := grp.FetchUsers(); err == nil {
		grp.users = usrs
	} else {
		return err
	}
	return nil
}
func (grp *Group) FetchInfo() (map[string]string, error) {
	var (
		result C.goRodsStringResult_t
		err    *C.char
	)
	result.size = C.int(0)
	cGroup := C.CString(grp.name)
	defer C.free(unsafe.Pointer(cGroup))
	ccon := grp.con.GetCcon()
	if status := C.gorods_get_user(cGroup, ccon, &result, &err); status != 0 {
		grp.con.ReturnCcon(ccon)
		return nil, newError(Fatal, status, fmt.Sprintf("iRODS Get Group Info Failed: %v", C.GoString(err)))
	}
	grp.con.ReturnCcon(ccon)
	defer C.gorods_free_string_result(&result)
	unsafeArr := unsafe.Pointer(result.strArr)
	arrLen := int(result.size)
	slice := (*[1 << 30]*C.char)(unsafeArr)[:arrLen:arrLen]
	response := make(map[string]string)
	for _, groupInfo := range slice {
		groupAttributes := strings.Split(strings.Trim(C.GoString(groupInfo), " \n"), "\n")
		for _, attr := range groupAttributes {
			split := strings.Split(attr, ": ")
			attrName := split[0]
			attrVal := split[1]
			response[attrName] = attrVal
		}
	}
	return response, nil
}
func (grp *Group) FetchUsers() (Users, error) {
	var (
		result C.goRodsStringResult_t
		err    *C.char
	)
	result.size = C.int(0)
	cGroupName := C.CString(grp.name)
	defer C.free(unsafe.Pointer(cGroupName))
	ccon := grp.con.GetCcon()
	if status := C.gorods_get_group(ccon, &result, cGroupName, &err); status != 0 {
		grp.con.ReturnCcon(ccon)
		if status == C.CAT_NO_ROWS_FOUND {
			return make(Users, 0), nil
		} else {
			return nil, newError(Fatal, status, fmt.Sprintf("iRODS Get Group %v Failed: %v", grp.name, C.GoString(err)))
		}
	}
	grp.con.ReturnCcon(ccon)
	defer C.gorods_free_string_result(&result)
	unsafeArr := unsafe.Pointer(result.strArr)
	arrLen := int(result.size)
	slice := (*[1 << 30]*C.char)(unsafeArr)[:arrLen:arrLen]
	if usrs, err := grp.con.Users(); err == nil {
		response := make(Users, 0)
		for _, userNames := range slice {
			usrFrags := strings.Split(C.GoString(userNames), "#")
			if usr := usrs.FindByName(usrFrags[0], grp.con); usr != nil {
				response = append(response, usr)
			} else {
				return nil, newError(Fatal, -1, fmt.Sprintf("iRODS FetchUsers Failed: User in response not found in cache"))
			}
		}
		return response, nil
	} else {
		return nil, err
	}
}
func (grp *Group) AddUser(usr interface{}) error {
	switch usr.(type) {
	case string:
		if usrs, err := grp.con.Users(); err == nil {
			usrName := usr.(string)
			if existingUsr := usrs.FindByName(usrName, grp.con); existingUsr != nil {
				zoneName := existingUsr.zone
				return addToGroup(usrName, zoneName, grp.name, grp.con)
			} else {
				return newError(Fatal, -1, fmt.Sprintf("iRODS AddUser Failed: can't find iRODS user by string"))
			}
		} else {
			return err
		}
	case *User:
		aUsr := usr.(*User)
		return addToGroup(aUsr.name, aUsr.zone, grp.name, aUsr.con)
	default:
	}
	return newError(Fatal, -1, fmt.Sprintf("iRODS AddUser Failed: unknown type passed"))
}
func (grp *Group) RemoveUser(usr interface{}) error {
	switch usr.(type) {
	case string:
		if usrs, err := grp.con.Users(); err == nil {
			usrName := usr.(string)
			if existingUsr := usrs.FindByName(usrName, grp.con); existingUsr != nil {
				zoneName := existingUsr.zone
				return removeFromGroup(usrName, zoneName, grp.name, grp.con)
			} else {
				return newError(Fatal, -1, fmt.Sprintf("iRODS RemoveUser Failed: can't find iRODS user by string"))
			}
		} else {
			return err
		}
	case *User:
		aUsr := usr.(*User)
		return removeFromGroup(aUsr.name, aUsr.zone, grp.name, aUsr.con)
	default:
	}
	return newError(Fatal, -1, fmt.Sprintf("iRODS RemoveUser Failed: unknown type passed"))
}