szaydel
11/17/2014 - 8:58 PM

Basic Example of using Interfaces for type conversion from strings to Struct

Basic Example of using Interfaces for type conversion from strings to Struct

// nativecmdr project nativecmdr.go
package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
)

const (
	ENOSLC        = "failed to produce slice of values"
	ENOMATRIX     = "there are no x,y elements in matrix"
	PYCMD         = "/usr/local/bin/python"
	PYARG_WORK    = "for i in range(0, 16): print('%d,%d' % (i, i*i))"
	PYARG_NO_WORK = "for i in range(0, 16): print('%d:%d' % (i, i*i))"
)

type Nativer interface {
	NativeType(s string) error
	HasError() bool
}

type MatrixArrayResult struct {
	Xprime   []int
	Yprime   []int
	Error    error
	ExitCode int
}

type CmdResult struct {
	Stdout   string
	Stderr   string
	ExitCode int
}

func (m *MatrixArrayResult) HasError() bool {
	if m.ExitCode == 0 {
		return false
	} else {
		return true
	}
}

func (m *MatrixArrayResult) NativeType(s string) error {

	var mkerr = func(s string) error {
		return fmt.Errorf("%v", s)
	}

	var strslc = strings.Split(s, "\n")
	if len(strslc) < 1 {
		m.Error = mkerr(ENOSLC)
		return mkerr(ENOSLC)
	}
	for _, val := range strslc {
		tm := strings.Split(val, ",")
		if len(tm) < 2 { // range errors are not what we want
			break
		}
		x, _ := strconv.Atoi(tm[0]) // Unchecked err
		y, _ := strconv.Atoi(tm[1]) // Unchecked err
		m.Xprime = append(m.Xprime, x)
		m.Yprime = append(m.Yprime, y)
	}

	if len(m.Xprime) < 1 || len(m.Yprime) < 1 {
		m.Error = mkerr(ENOMATRIX)
		return mkerr(ENOMATRIX)
	}

	return nil
}

func ErrHandle(e error) error {
	if e != nil {
		log.Printf("%v", fmt.Errorf("%v", e.Error()))
		return e
	}
	return nil
}

func RunCmd(ex Nativer, command string, args ...string) error {
	var result CmdResult

	cmd := exec.Command(command, args...)

	// Stdout buffer
	cmdOutput := &bytes.Buffer{}

	// Attach buffer to command
	cmd.Stdout = cmdOutput

	//Stderr buffer
	cmdError := &bytes.Buffer{}

	//Attach buffer to cmd
	cmd.Stderr = cmdError

	var waitStatus syscall.WaitStatus

	if err := cmd.Run(); err != nil {
		//Grab stderr
		result.Stderr = string(cmdError.Bytes())

		// Did the command fail because of an unsuccessful exit code
		if exitError, ok := err.(*exec.ExitError); ok {

			waitStatus = exitError.Sys().(syscall.WaitStatus)

			//Store exit code
			result.ExitCode = waitStatus.ExitStatus()
		} else {
			//There was not exit error because the command never ran.
			//Let's set an error ourselves so there is no confusion.
			result.Stderr = fmt.Sprintf("error running command: %s", command)

			//Store exit code for Bash command not found
			result.ExitCode = 127
		}
	} else {
		//Grab stdout and stuff it into return stdout as string
		result.Stdout = string(cmdOutput.Bytes())

		// Command was successful
		waitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus)

		//Store exit code
		result.ExitCode = waitStatus.ExitStatus()
	}

	if e := ex.NativeType(result.Stdout); e != nil {
		return nil // In reality this should return `e` instead of nil
	}

	return nil
}

func main() {
	var Amatrix Nativer = &MatrixArrayResult{}
	var Bmatrix Nativer = &MatrixArrayResult{}
	//var Amatrix = Nativer(&MatrixArrayResult{}) // A bit more ugly than above
	//var Bmatrix = Nativer(&MatrixArrayResult{})

	// This should work, Nil error
	if e := RunCmd(Amatrix, PYCMD, "-c", PYARG_WORK); e == nil {
		fmt.Printf("Matrix Xs: %v\nMatrix Ys: %v\nError: %v\n",
			Amatrix.(*MatrixArrayResult).Xprime, // assertions must be checked
			Amatrix.(*MatrixArrayResult).Yprime,
			Amatrix.(*MatrixArrayResult).Error)
	}

	// This should not work, non-Nil error
	if e := RunCmd(Bmatrix, PYCMD, "-c", PYARG_NO_WORK); e == nil {
		fmt.Printf("Matrix Xs: %v\nMatrix Ys: %v\nError: %v\n",
			Bmatrix.(*MatrixArrayResult).Xprime, // assertions must be checked
			Bmatrix.(*MatrixArrayResult).Yprime,
			Bmatrix.(*MatrixArrayResult).Error)
	}
	//
	// Results of running above should look something like this:
	//
	//Matrix Xs: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
	//Matrix Ys: [0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225]
	//Error: <nil>
	//Matrix Xs: []
	//Matrix Ys: []
	//Error: there are no x,y elements in matrix

}
// mknativetype project main.go
package main

import (
	"fmt"
	"strconv"
	"strings"
)

type Nativer interface {
	NativeType(s string) error
}

type Foo struct {
	Fruit string
	Veg   string
	Array []byte
}

type Bar struct {
	First string
	Last  string
	Array []byte
}

type Matrix struct {
	X int
	Y int
}

func (m *Matrix) NativeType(s string) error {
	var sep = ","
	var strslc = strings.SplitN(s, sep, 4)
	m.X, _ = strconv.Atoi(strslc[0])
	m.Y, _ = strconv.Atoi(strslc[2])
	return nil
}

func (f *Foo) NativeType(s string) error {
	var sep = ";"
	var strslc = strings.SplitN(s, sep, 3)
	f.Fruit = strslc[0]
	f.Veg = strslc[1]
	f.Array = []byte(s)
	return nil
}

func (b *Bar) NativeType(s string) error {
	var sep = " "
	var strslc = strings.SplitN(s, sep, 5)
	b.First = strslc[2]
	b.Last = strslc[3]
	b.Array = []byte(fmt.Sprintf("%s %s", strslc[2], strslc[3]))
	return nil
}

func MimicCommand(s string, n Nativer) error {
	err := n.NativeType(s)
	if err != nil {
		return fmt.Errorf("%s", "poof")
	}
	return nil
}

func main() {
	var f1 Nativer = &Foo{}
	var b1 Nativer = &Bar{}
	var m1 Nativer = &Matrix{}

	MimicCommand("apple;detroit-red beet;banana;sweet potato", f1)
	MimicCommand("junk more.junk Joe Buckeye is a Ducks Fan", b1)
	MimicCommand("100,-0.5,20,-0.3", m1)

	fmt.Printf("Food -- Fruit: %v Vegetable: %v Array: %v\n", f1.(*Foo).Fruit, f1.(*Foo).Veg, f1.(*Foo).Array)
	fmt.Printf("Name -- First: %v Last: %v Array: %v\n", b1.(*Bar).First, b1.(*Bar).Last, b1.(*Bar).Array)
	fmt.Printf("Matrix -- X: %v Y: %v\n", m1.(*Matrix).X, m1.(*Matrix).Y)

}