// Decode converts Sass Value to Go compatible data types. func Unmarshal(arg UnionSassValue, v ...interface{}) error { var err error if arg == nil { return errors.New("I can't work with this. arg UnionSassValue must not be nil. - Unmarshaller") } else if len(v) == 0 { return errors.New("Cannot Unmarshal an empty value - Michael Scott") } else if len(v) > 1 { if len(v) < int(C.sass_list_get_length(arg)) { //check for optional arguements that are not passed and pad with nil return fmt.Errorf("Arguments mismatch %d C arguments did not match %d", int(C.sass_list_get_length(arg)), len(v)) } for i := 0; i < int(C.sass_list_get_length(arg)); i++ { err = unmarshal(C.sass_list_get_value(arg, C.size_t(i)), v[i]) if err != nil { return err } } return err } else if C.sass_value_is_list(arg) && getKind(v[0]) != reflect.Slice && int(C.sass_list_get_length(arg)) == 1 { //arg is a slice of 1 but we want back a non slice return unmarshal(C.sass_list_get_value(arg, C.size_t(0)), v[0]) } else if C.sass_value_is_list(arg) && getKind(v[0]) == reflect.Slice && C.sass_value_is_list(C.sass_list_get_value(arg, C.size_t(0))) && int(C.sass_list_get_length(arg)) == 1 { //arg is a list of single list and we only want back a list so we need to unwrap return unmarshal(C.sass_list_get_value(arg, C.size_t(0)), v[0]) } else { return unmarshal(arg, v[0]) } }
func unmarshal(arg UnionSassValue, v interface{}) error { //Get the underlying value of v and its kind f := reflect.ValueOf(v) if f.Kind() == reflect.Ptr { f = f.Elem() } k := f.Kind() t := f.Type() if k == reflect.Interface { switch { default: return errors.New("Uncovertable interface value. Specify type desired.") case bool(C.sass_value_is_null(arg)): f.Set(reflect.ValueOf("<nil>")) return nil case bool(C.sass_value_is_string(arg)): k = reflect.String case bool(C.sass_value_is_boolean(arg)): k = reflect.Bool case bool(C.sass_value_is_color(arg)) || bool(C.sass_value_is_number(arg)): k = reflect.Struct case bool(C.sass_value_is_list(arg)): k = reflect.Slice t = reflect.SliceOf(t) case bool(C.sass_value_is_error(arg)): // This should get implemented as type error k = reflect.String } } switch k { default: return errors.New("Unsupported SassValue") case reflect.Invalid: return errors.New("Invalid SASS Value - Taylor Swift") case reflect.String: if C.sass_value_is_string(arg) || C.sass_value_is_error(arg) { c := C.sass_string_get_value(arg) gc := C.GoString(c) //drop quotes if t, err := strconv.Unquote(gc); err == nil { gc = t } if strings.HasPrefix(gc, "'") && strings.HasSuffix(gc, "'") { gc = gc[1 : len(gc)-1] } if !f.CanSet() { return errors.New("Can not set string") } switch t := f.Kind(); t { case reflect.String: f.SetString(gc) case reflect.Interface: f.Set(reflect.ValueOf(gc)) } } else { return throwMisMatchTypeError(arg, "string") } case reflect.Bool: if C.sass_value_is_boolean(arg) { b := bool(C.sass_boolean_get_value(arg)) f.Set(reflect.ValueOf(b)) } else { return throwMisMatchTypeError(arg, "bool") } case reflect.Struct: //Check for color if C.sass_value_is_color(arg) { col := color.RGBA{ R: uint8(C.sass_color_get_r(arg)), G: uint8(C.sass_color_get_g(arg)), B: uint8(C.sass_color_get_b(arg)), A: uint8(C.sass_color_get_a(arg)), } f.Set(reflect.ValueOf(col)) } else if C.sass_value_is_number(arg) { u, err := getSassNumberUnit(arg) if err != nil { return err } sn := SassNumber{ Value: float64(C.sass_number_get_value(arg)), Unit: u, } f.Set(reflect.ValueOf(sn)) } else { return throwMisMatchTypeError(arg, "color.RGBA or SassNumber") } case reflect.Slice: if C.sass_value_is_list(arg) { newv := reflect.MakeSlice(t, int(C.sass_list_get_length(arg)), int(C.sass_list_get_length(arg))) l := make([]interface{}, C.sass_list_get_length(arg)) for i := range l { err := unmarshal(C.sass_list_get_value(arg, C.size_t(i)), &l[i]) if err != nil { return err } newv.Index(i).Set(reflect.ValueOf(l[i])) } f.Set(newv) } else { return throwMisMatchTypeError(arg, "slice") } } return nil }
func IsList(usv UnionSassValue) bool { return bool(C.sass_value_is_list(usv)) }