// Package rules has the unit conversion rules. package rules import ( "embed" "errors" "fmt" "github.com/ichiban/prolog" ) // Engine is the rules engine. type Engine struct { p *prolog.Interpreter } //go:embed source/* var src embed.FS // New creates a new rules engine. func New() (*Engine, error) { ngn := &Engine{ p: prolog.New(nil, nil), } // Treat a string argument as an atom. err := ngn.p.Exec(`:- set_prolog_flag(double_quotes, atom).`) if err != nil { return nil, err } rules, err := src.ReadFile("source/rules.pdb") if err != nil { return nil, err } err = ngn.p.Exec(string(rules)) if err != nil { return nil, err } return ngn, nil } // Kind returns the kind of unit. func (ngn *Engine) Kind(unit string) (string, error) { sols, err := ngn.p.Query("unit_kind(?, Kind).", unit) if err != nil { return "", err } defer sols.Close() var result string for sols.Next() { var kind struct { Kind string } if err := sols.Scan(&kind); err != nil { return "", err } if result != "" { return "", errors.New("Unit has multiple kinds") } result = kind.Kind } return result, nil } // Convert converts units. func (ngn *Engine) Convert(qty float64, givenUnit, targetUnit string) (float64, error) { sol := ngn.p.QuerySolution("convert(?, ?, Qty, ?).", qty, givenUnit, targetUnit) if sol == nil { fmt.Println("sol == nil") return 0, errors.New("Query failed") } err := sol.Err() if err != nil { fmt.Println("sol.Err()") return 0, err } var quantity struct { Qty float64 } err = sol.Scan(&quantity) if err == nil { fmt.Println("got qty") return quantity.Qty, err } fmt.Println("qty scan failed") var dbg []byte if err := sol.Scan(&dbg); err != nil { fmt.Println("on dbg scan") return 0, err } fmt.Printf("dbg: '%s'\n", dbg) return 0, err }