okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
f65a648
main
1package daemon 2 3import ( 4"context" 5"encoding/json" 6"errors" 7"maps" 8"strings" 9"unicode" 10) 11 12type CommandResult struct { 13Effort * EffortResult 14Page * PageDocument 15Message string 16Result json.RawMessage 17Submitted bool 18} 19 20func ListSessionCommands (ctx context.Context ,conn * Connection ,session string ) ([]SessionCommand ,error ) { 21catalog ,err := readCapabilityCatalog (ctx ,conn ,session ,false ) 22if err != nil { 23return nil ,err 24 } 25return catalog .Commands ,nil 26} 27 28// InvokeDeclaredCommand follows the catalog's HTTP operation or admits its typed input. 29func InvokeDeclaredCommand (ctx context.Context ,conn * Connection ,snapshot Session ,command SessionCommand ,arguments map [string ]json.RawMessage ) (CommandResult ,error ) { 30if command .Delivery == "input" { 31encoded ,err := json .Marshal (arguments ) 32if err != nil { 33return CommandResult {},err 34 } 35request := SubmissionRequest {Type :"command" ,Name :command .CommandID ,CommandArguments :encoded } 36handle ,err := NewSubmission (snapshot .ID ,request ) 37if err != nil { 38return CommandResult {},err 39 } 40_ ,err = SubmitOperation (ctx ,conn ,handle ) 41return CommandResult {Submitted :err == nil },err 42 } 43if command .Operation .PathTemplate == "" { 44return CommandResult {},errors .New ("the command has no declared HTTP operation" ) 45 } 46data ,err := executeBoundOperation (ctx ,conn ,command .Operation ,nil ,arguments ,& snapshot ) 47if err != nil { 48return CommandResult {},err 49 } 50if command .Delivery == "read" { 51page ,err := readResultPage (data ,command .Name ,command .Description ) 52if err != nil { 53return CommandResult {},err 54 } 55page .Session = & snapshot 56page .read = & pageRead {Name :command .Name ,Description :command .Description ,Operation :command .Operation ,Form :maps .Clone (arguments )} 57return CommandResult {Result :data ,Page :page },nil 58 } 59return extensionResult (data ),nil 60} 61 62// ParseCommandArguments reads the text typed after a command name. Words fill 63// the declared arguments in order and the last argument takes the rest of the 64// line; a JSON object names them instead. 65func ParseCommandArguments (command SessionCommand ,text string ) (map [string ]json.RawMessage ,error ) { 66text = strings .TrimSpace (text ) 67if len (command .Arguments )> 1 && strings .HasPrefix (text ,"{" ) { 68return namedCommandArguments (command ,text ) 69 } 70result := map [string ]json.RawMessage {} 71for index ,argument := range command .Arguments { 72word := text 73if index < len (command .Arguments )- 1 { 74word ,text = cutWord (text ) 75 }else { 76text = "" 77 } 78if word == "" && ! argument .Required { 79continue 80 } 81value ,err := actionFieldValue (argument .field ,word ) 82if err != nil { 83return nil ,err 84 } 85result [argument .Name ]= value 86 } 87if text != "" { 88return nil ,errors .New (command .Name + " takes no arguments" ) 89 } 90return result ,nil 91} 92 93func cutWord (text string ) (string ,string ) { 94end := strings .IndexFunc (text ,unicode .IsSpace ) 95if end < 0 { 96return text ,"" 97 } 98return text [:end ],strings .TrimSpace (text [end :]) 99} 100 101func namedCommandArguments (command SessionCommand ,text string ) (map [string ]json.RawMessage ,error ) { 102result := map [string ]json.RawMessage {} 103if err := json .Unmarshal ([]byte (text ),& result );err != nil { 104return nil ,errors .New ("this command accepts words in argument order or a JSON object with its declared argument names" ) 105 } 106for _ ,field := range command .Arguments { 107raw ,ok := result [field .Name ] 108if ok { 109if err := validateFormValue (field .field ,raw );err != nil { 110return nil ,err 111 } 112 } 113if ! ok && field .Required { 114return nil ,errors .New ("missing command argument: " + field .Name ) 115 } 116 } 117for name := range result { 118known := false 119for _ ,field := range command .Arguments { 120known = known || field .Name == name 121 } 122if ! known { 123return nil ,errors .New ("unknown command argument: " + name ) 124 } 125 } 126return result ,nil 127}