diff --git a/example/my-app/README.md b/example/my-app/README.md index 2842a638..edc17f63 100644 --- a/example/my-app/README.md +++ b/example/my-app/README.md @@ -2,5 +2,7 @@ ``` npm start -go run server.go functions.go +export OPENAI_API_KEY=sk- +go generate ./... +go run *.go ``` diff --git a/example/my-app/src/App.css b/example/my-app/src/App.css index 293e6fc0..5fe6895d 100644 --- a/example/my-app/src/App.css +++ b/example/my-app/src/App.css @@ -30,12 +30,13 @@ span.error { span.await { background-color: #52d2d2; } -input { + +input, select { padding: 5px; margin: 0 5px 0 0; } -input, button { +input, button, select, textarea { border: 1px solid #51887a; border-radius: 0; margin-top: 2px; @@ -97,6 +98,18 @@ table > thead .option-row button { margin-right: 5px; } +table > thead .filter-rules { + float: right; +} + +.filter-rules { + +} + +.filter-group, .filter-rule { + margin-left: 40px; +} + table > tbody > tr { border-bottom: 1px solid #dddddd; } @@ -109,6 +122,21 @@ tbody tr:last-of-type { border-bottom: 2px solid #009879; } +tfoot tr { + background-color: #ffffff; + color: #ffffff; + text-align: left; + font-size: 1.2em; +} + +button.next-page{ + float: right; +} +button.prev-page{ + float: left; +} + + /* tables inside a table should be smaller */ table table { font-size: 0.8em; diff --git a/example/my-app/src/App.tsx b/example/my-app/src/App.tsx index 03b13213..20ad6b25 100644 --- a/example/my-app/src/App.tsx +++ b/example/my-app/src/App.tsx @@ -1,10 +1,12 @@ -import React, {useEffect, useReducer, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import './App.css'; import * as openai from './workflow/github_com_sashabaranov_go-openai' import * as schemaless from './workflow/github_com_widmogrod_mkunion_x_storage_schemaless' import * as workflow from './workflow/github_com_widmogrod_mkunion_x_workflow' +import * as predicate from "./workflow/github_com_widmogrod_mkunion_x_storage_predicate"; import * as schema from "./workflow/github_com_widmogrod_mkunion_x_schema"; import {Chat} from "./Chat"; +import {PaginatedTable} from "./component/PaginatedTable"; function flowCreate(flow: workflow.Flow) { return fetch('http://localhost:8080/flow', { @@ -30,6 +32,10 @@ type ListProps = { [key: string]: boolean } limit?: number, + where?: predicate.WherePredicates + + prevPage?: string, + nextPage?: string, } function storageList(input: ListProps): Promise>> { @@ -45,7 +51,10 @@ function storageList(input: ListProps): Promise>), }) .then(res => res.json()) @@ -54,17 +63,15 @@ function storageList(input: ListProps): Promise) { return storageList({ + ...input, path: "states", - sort: input?.sort, - limit: input?.limit, }) } function listFlows(input?: ListProps) { return storageList({ + ...input, path: "flows", - sort: input?.sort, - limit: input?.limit, }) } @@ -720,8 +727,7 @@ function App() { load={(state) => { return listFlows({ - limit: state.limit, - sort: state.sort, + ...state, }) }} mapData={(data) => { @@ -745,8 +751,7 @@ function App() { load={(state) => { return listStates({ - limit: state.limit, - sort: state.sort, + ...state, }) }} actions={[ @@ -779,7 +784,7 @@ function App() { } } ]} - mapData={(input) => { + mapData={(input, ctx) => { if (!input.Data) { return
nothing
} @@ -804,6 +809,46 @@ function App() { case "schema.String": return <> workflow.Done + + {done.Result["schema.String"]} @@ -835,8 +880,29 @@ function App() { return ( <> workflow.Await + + @@ -1067,202 +1133,6 @@ function SchemaValue(props: { data?: schema.Schema }) { return } -type PaginatedTableState = { - limit: number - sort: PaginatedTableSort - selected: { [key: string]: schemaless.Record } -} - -type PaginatedTableSort = { - [key: string]: boolean -} - -type PaginatedTableAction = { - name: string - action: (state: PaginatedTableState, ctx: PaginatedTableContext) => void -} - -type PaginatedTableContext = { - refresh: () => void - clearSelection: () => void -} - - -type PaginatedTableProps = { - limit?: number - sort?: PaginatedTableSort - load: (input: PaginatedTableState) => Promise>> - mapData?: (data: schemaless.Record) => JSX.Element - actions?: PaginatedTableAction[] -} - -function PaginatedTable(props: PaginatedTableProps) { - const [data, setData] = useState({Items: [] as any[]} as schemaless.PageResult>) - const [state, setState] = useState({ - limit: props.limit || 30, - sort: props.sort || {}, - selected: {}, - } as PaginatedTableState) - - useEffect(() => { - props.load(state).then(setData) - }, [state]) - - const ctx = { - refresh: () => { - props.load(state).then(setData) - }, - clearSelection: () => { - setState({ - ...state, - selected: {}, - }) - } - } as PaginatedTableContext - - const changeSort = (key: string) => (e: React.MouseEvent) => { - e.preventDefault() - - let newSort = {...state.sort} - if (newSort[key] === undefined) { - newSort[key] = true - } else if (newSort[key]) { - newSort[key] = false - } else { - delete newSort[key] - } - - setState({ - ...state, - sort: newSort, - }) - } - - const sortState = (key: string) => { - if (state.sort[key] === undefined) { - return "sort-none" - } else if (state.sort[key]) { - return "sort-asc" - } else { - return "sort-desc" - } - } - - - const selectRowToggle = (item: schemaless.Record) => () => { - if (!item.ID) { - return - } - - let selected = {...state.selected} - if (selected[item.ID]) { - delete selected[item.ID] - } else { - selected[item.ID] = item - } - - setState({ - ...state, - selected: selected, - }) - } - - const isSelected = (item: schemaless.Record) => { - if (!item.ID) { - return false - } - - return state.selected[item.ID] !== undefined - } - - const batchSelection = (e: React.MouseEvent) => { - e.preventDefault() - - let selectionLength = Object.keys(state.selected).length - if (selectionLength > 0) { - setState({ - ...state, - selected: {}, - }) - } else { - let selected = {} as { [key: string]: schemaless.Record } - data.Items?.forEach((item) => { - if (!item.ID) { - return - } - - selected[item.ID] = item - }) - - setState({ - ...state, - selected: selected - } as PaginatedTableState) - } - } - - const batchSelectionState = () => { - let selectionLength = Object.keys(state.selected).length - if (selectionLength === 0) { - return "selected-none" - } - if (selectionLength === data.Items?.length) { - return "selected-all" - } - - return "selected-some" - } - - const applyAction = (action: PaginatedTableAction) => (e: React.MouseEvent) => { - e.preventDefault() - action.action(state, ctx) - } - - return - - - - - - - - - - - - - - {data.Items && data.Items.length > 0 ? data.Items.map((item) => { - return ( - - - - - - - - ); - }) : ( - - - - )} - -
- - - {props.actions && props.actions.map((action) => { - return - })} -
- - IDTypeVersionData
{item.ID}{item.Type}{item.Version}{props.mapData && props.mapData(item)}
No data
-} - function WorkflowToString(props: { flow?: workflow.Workflow }) { const [str, setStr] = useState("") diff --git a/example/my-app/src/component/PaginatedTable.tsx b/example/my-app/src/component/PaginatedTable.tsx new file mode 100644 index 00000000..2ec9f71f --- /dev/null +++ b/example/my-app/src/component/PaginatedTable.tsx @@ -0,0 +1,505 @@ +import React, {useEffect, useState} from "react"; +import * as schemaless from "../workflow/github_com_widmogrod_mkunion_x_storage_schemaless"; +import * as schema from "../workflow/github_com_widmogrod_mkunion_x_schema"; +import * as predicate from "../workflow/github_com_widmogrod_mkunion_x_storage_predicate"; + +export type Cursor = string + +export type PaginatedTableState = { + limit: number + sort: PaginatedTableSort + selected: { [key: string]: schemaless.Record } + + prevPage?: Cursor + nextPage?: Cursor + + where?: predicate.WherePredicates +} + +export type PaginatedTableSort = { + [key: string]: boolean +} + +export type PaginatedTableAction = { + name: string + action: (state: PaginatedTableState, ctx: PaginatedTableContext) => void +} + +export type PaginatedCompare = { + location: string + operation: "==" | "!=" | "<" | "<=" | ">" | ">=" + literal: schema.Schema +} + +export type PaginatedTableContext = { + refresh: () => void + clearSelection: () => void + filter: (x: predicate.WherePredicates) => void +} + + +export type PaginatedTableProps = { + limit?: number + sort?: PaginatedTableSort + load: (input: PaginatedTableState) => Promise>> + mapData?: (data: schemaless.Record, ctx: PaginatedTableContext) => JSX.Element + actions?: PaginatedTableAction[] +} + +export function PaginatedTable(props: PaginatedTableProps) { + const [data, setData] = useState({Items: [] as any[]} as schemaless.PageResult>) + const [state, setState] = useState({ + limit: props.limit || 3, + sort: props.sort || {}, + selected: {}, + } as PaginatedTableState) + + useEffect(() => { + props.load(state).then(setData) + }, [state]) + + const ctx = { + refresh: () => { + props.load(state).then(setData) + }, + clearSelection: () => { + setState({ + ...state, + selected: {}, + }) + }, + filter: (x: predicate.WherePredicates) => { + setState({ + ...state, + where: mergeFilters(state.where, x) + }) + } + } as PaginatedTableContext + + const changeSort = (key: string) => (e: React.MouseEvent) => { + e.preventDefault() + + let newSort = {...state.sort} + if (newSort[key] === undefined) { + newSort[key] = true + } else if (newSort[key]) { + newSort[key] = false + } else { + delete newSort[key] + } + + setState({ + ...state, + sort: newSort, + }) + } + + const sortState = (key: string) => { + if (state.sort[key] === undefined) { + return "sort-none" + } else if (state.sort[key]) { + return "sort-asc" + } else { + return "sort-desc" + } + } + + + const selectRowToggle = (item: schemaless.Record) => () => { + if (!item.ID) { + return + } + + let selected = {...state.selected} + if (selected[item.ID]) { + delete selected[item.ID] + } else { + selected[item.ID] = item + } + + setState({ + ...state, + selected: selected, + }) + } + + const isSelected = (item: schemaless.Record) => { + if (!item.ID) { + return false + } + + return state.selected[item.ID] !== undefined + } + + const batchSelection = (e: React.MouseEvent) => { + e.preventDefault() + + let selectionLength = Object.keys(state.selected).length + if (selectionLength > 0) { + setState({ + ...state, + selected: {}, + }) + } else { + let selected = {} as { [key: string]: schemaless.Record } + data.Items?.forEach((item) => { + if (!item.ID) { + return + } + + selected[item.ID] = item + }) + + setState({ + ...state, + selected: selected + } as PaginatedTableState) + } + } + + const batchSelectionState = () => { + let selectionLength = Object.keys(state.selected).length + if (selectionLength === 0) { + return "selected-none" + } + if (selectionLength === data.Items?.length) { + return "selected-all" + } + + return "selected-some" + } + + const applyAction = (action: PaginatedTableAction) => (e: React.MouseEvent) => { + e.preventDefault() + action.action(state, ctx) + } + + const nextPage = (e: React.MouseEvent) => { + e.preventDefault() + setState({ + ...state, + nextPage: data.Next?.After, + prevPage: undefined, + }) + } + + const prevPage = (e: React.MouseEvent) => { + e.preventDefault() + setState({ + ...state, + nextPage: undefined, + prevPage: data.Prev?.Before, + }) + } + + return + + + + + + + + + + + + + + {data.Items && data.Items.length > 0 ? data.Items.map((item) => { + return ( + + + + + + + + ); + }) : ( + + + + )} + + + + + + +
+ + + {props.actions && props.actions.map((action) => { + return + })} + + { + setState({ + ...state, + where: where, + }) + }}/> +
+ + IDTypeVersionData
{item.ID}{item.Type}{item.Version}{props.mapData && props.mapData(item, ctx)}
No data
+ {data.Next && } + {data.Prev && } +
+} + +function mergeFilters(a?: predicate.WherePredicates, b?: predicate.WherePredicates): predicate.WherePredicates | undefined { + if (a === undefined) { + return b + } + if (b === undefined) { + return a + } + + let result = {...a} + result.Params = {...a.Params, ...b.Params} + result.Predicate = mergePredicate(a.Predicate, b.Predicate) + + return result +} + +function mergePredicate(a?: predicate.Predicate, b?: predicate.Predicate): predicate.Predicate | undefined { + if (a === undefined) { + return b + } + if (b === undefined) { + return a + } + + return { + "$type": "predicate.And", + "predicate.And": { + L: [a, b], + }, + } +} + + +function WherePredicateRender(props: { + where?: predicate.WherePredicates, + onChange: (where?: predicate.WherePredicates) => void +}) { + if (!props.where) { + return <> + } + + return
+ { + if (!where) { + props.onChange(undefined) + return + } else { + props.onChange({ + ...props.where, + Predicate: where, + }) + } + }}/> +
+} + +function PredicateRender(props: { + predicate?: predicate.Predicate, + onChange?: (where?: predicate.Predicate) => void +}) { + if (!props.predicate) { + return <> + } + + switch (props.predicate.$type) { + case "predicate.And": + let and = props.predicate["predicate.And"] + + if (!and.L) { + return <> + } + + return
+ + (AND + {and.L?.map((x) => { + return { + let predicates = and.L?.map((y) => (y === x) ? where : y).filter((y) => y !== undefined) as predicate.Predicate[] + + if (predicates.length === 0) { + props.onChange && props.onChange(undefined) + return + } + + props.onChange && props.onChange({ + "$type": "predicate.And", + "predicate.And": { + L: predicates + } + }) + }} + /> + })} + ) +
+ + case "predicate.Or": + let or = props.predicate["predicate.Or"] + + if (!or.L) { + return <> + } + + return
+ or {or.L?.map((x) => { + return + }).join(" or ")} +
+ + case "predicate.Not": + let not = props.predicate["predicate.Not"] + + if (!not.P) { + return <> + } + + return
+ not +
+ + case "predicate.Compare": + let compare = props.predicate["predicate.Compare"] + + return
+ + + + +
+ } + + return
Unknown predicate
+} + +function BindableValueRender(props: { bindable?: predicate.Bindable }) { + if (!props.bindable) { + return <> + } + + switch (props.bindable.$type) { + case "predicate.BindValue": + let value = props.bindable["predicate.BindValue"] + + return + + case "predicate.Literal": + let literal = props.bindable["predicate.Literal"] + + return + + case "predicate.Locatable": + let locatable = props.bindable["predicate.Locatable"] + + return
+ {locatable.Location} +
+ } + + return
Unknown bindable {JSON.stringify(props.bindable)}
+} + +function SchemaValue(props: { data?: schema.Schema }) { + if (!props.data) { + return <> + } + + switch (props.data.$type) { + case "schema.String": + return + case "schema.Number": + return <>{props.data["schema.Number"]} + case "schema.Binary": + return <>binary + case "schema.Bool": + return <>{props.data["schema.Bool"]} + case "schema.List": + const listData = props.data["schema.List"]; + return ( +
    + {listData && listData.map((item, index) => ( +
  • + +
  • + ))} +
+ ); + + case "schema.Map": + const mapData = props.data["schema.Map"]; + const keys = Object.keys(mapData); + + if (keys && keys.length === 0) { + return <>; // If the map is empty, return null (no table to display) + } + + return ( + + + + + + + + + {keys && keys.map((key) => ( + + + + + ))} + +
KeyValue
{key} + +
+ ); + + } + + return
+ Unknown schema {JSON.stringify(props.data)} +
+} \ No newline at end of file diff --git a/example/my-app/src/workflow/github_com_widmogrod_mkunion_exammple_my-app.ts b/example/my-app/src/workflow/github_com_widmogrod_mkunion_exammple_my-app.ts index b1c554f4..369a9fa8 100644 --- a/example/my-app/src/workflow/github_com_widmogrod_mkunion_exammple_my-app.ts +++ b/example/my-app/src/workflow/github_com_widmogrod_mkunion_exammple_my-app.ts @@ -32,23 +32,26 @@ export type ChatResponses = { Responses?: ChatResult[], } -export type RefreshFlows = {} - -export type Reshaper = workflow.Reshaper +export type FindRecords = schemaless.FindingRecords> export type FunctionOutput = workflow.FunctionOutput -export type RefreshStates = {} +export type UpdateRecords = schemaless.UpdateRecords> -export type PageResult = schemaless.PageResult> +export type FunctionInput = workflow.FunctionInput -export type FindRecords = schemaless.FindingRecords> +export type RefreshFlows = {} + +export type GenerateImage = { + Width?: number, + Height?: number, +} export type Schema = schema.Schema -export type UpdateRecords = schemaless.UpdateRecords> +export type RefreshStates = {} -export type FunctionInput = workflow.FunctionInput +export type Service = {} export type ListWorkflowsFn = { Count?: number, @@ -56,29 +59,26 @@ export type ListWorkflowsFn = { EnumTest?: string, } -export type GenerateImage = { - Width?: number, - Height?: number, -} +export type Command = workflow.Command -export type Workflow = workflow.Workflow +export type Reshaper = workflow.Reshaper -export type Predicate = workflow.Predicate +export type Workflow = workflow.Workflow -export type Command = workflow.Command +export type State = workflow.State export type Expr = workflow.Expr -export type State = workflow.State +export type Predicate = workflow.Predicate -export type Service = {} +export type PageResult = schemaless.PageResult> //eslint-disable-next-line -import * as openai from './github_com_sashabaranov_go-openai' +import * as schema from './github_com_widmogrod_mkunion_x_schema' //eslint-disable-next-line -import * as workflow from './github_com_widmogrod_mkunion_x_workflow' +import * as openai from './github_com_sashabaranov_go-openai' //eslint-disable-next-line import * as schemaless from './github_com_widmogrod_mkunion_x_storage_schemaless' //eslint-disable-next-line -import * as schema from './github_com_widmogrod_mkunion_x_schema' +import * as workflow from './github_com_widmogrod_mkunion_x_workflow' diff --git a/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_storage_schemaless.ts b/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_storage_schemaless.ts index 1e5ed36a..b8d742e9 100644 --- a/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_storage_schemaless.ts +++ b/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_storage_schemaless.ts @@ -1,15 +1,11 @@ //generated by mkunion -export type PageResult = { - Items?: A[], - Next?: FindingRecords, -} - export type FindingRecords = { RecordType?: string, Where?: predicate.WherePredicates, Sort?: SortField[], Limit?: number, After?: Cursor, + Before?: Cursor, } export type SortField = { @@ -34,6 +30,12 @@ export type UpdateRecords = { export type UpdatingPolicy = number +export type PageResult = { + Items?: A[], + Next?: FindingRecords, + Prev?: FindingRecords, +} + //eslint-disable-next-line import * as predicate from './github_com_widmogrod_mkunion_x_storage_predicate' diff --git a/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_workflow.ts b/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_workflow.ts index cae23022..075aa28f 100644 --- a/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_workflow.ts +++ b/example/my-app/src/workflow/github_com_widmogrod_mkunion_x_workflow.ts @@ -1,20 +1,4 @@ //generated by mkunion -export type Reshaper = { - "$type"?: "workflow.GetValue", - "workflow.GetValue": GetValue -} | { - "$type"?: "workflow.SetValue", - "workflow.SetValue": SetValue -} - -export type GetValue = { - Path?: string, -} - -export type SetValue = { - Value?: schema.Schema, -} - export type FunctionOutput = { Result?: schema.Schema, } @@ -25,6 +9,29 @@ export type FunctionInput = { Args?: schema.Schema[], } +export type Command = { + "$type"?: "workflow.Run", + "workflow.Run": Run +} | { + "$type"?: "workflow.Callback", + "workflow.Callback": Callback +} | { + "$type"?: "workflow.TryRecover", + "workflow.TryRecover": TryRecover +} | { + "$type"?: "workflow.StopSchedule", + "workflow.StopSchedule": StopSchedule +} | { + "$type"?: "workflow.ResumeSchedule", + "workflow.ResumeSchedule": ResumeSchedule +} + +export type Run = { + Flow?: Workflow, + Input?: schema.Schema, + RunOption?: RunOption, +} + export type Workflow = { "$type"?: "workflow.Flow", "workflow.Flow": Flow @@ -58,6 +65,22 @@ export type End = { Result?: Reshaper, } +export type Reshaper = { + "$type"?: "workflow.GetValue", + "workflow.GetValue": GetValue +} | { + "$type"?: "workflow.SetValue", + "workflow.SetValue": SetValue +} + +export type GetValue = { + Path?: string, +} + +export type SetValue = { + Value?: schema.Schema, +} + export type Assign = { ID?: string, VarOk?: string, @@ -119,29 +142,6 @@ export type FlowRef = { FlowID?: string, } -export type Command = { - "$type"?: "workflow.Run", - "workflow.Run": Run -} | { - "$type"?: "workflow.Callback", - "workflow.Callback": Callback -} | { - "$type"?: "workflow.TryRecover", - "workflow.TryRecover": TryRecover -} | { - "$type"?: "workflow.StopSchedule", - "workflow.StopSchedule": StopSchedule -} | { - "$type"?: "workflow.ResumeSchedule", - "workflow.ResumeSchedule": ResumeSchedule -} - -export type Run = { - Flow?: Workflow, - Input?: schema.Schema, - RunOption?: RunOption, -} - export type RunOption = { "$type"?: "workflow.ScheduleRun", "workflow.ScheduleRun": ScheduleRun diff --git a/example/my-app/test.http b/example/my-app/test.http deleted file mode 100644 index 34fab71d..00000000 --- a/example/my-app/test.http +++ /dev/null @@ -1,12 +0,0 @@ -### example request -POST http://instant-answer.external.platform-services-answerbot-dev.z-dn.net/v2 -Content-Type: application/json -Accept: application/json - -{ - "accessKey": "7890f5c3-7147-434e-84f9-1e0351351c31", - "prompt": "who whas napoleon bonaparte?", - "market": "us", - "subject": "history" -} - diff --git a/x/generators/shape_tagged.go b/x/generators/shape_tagged.go index 2e0f9c79..361a72df 100644 --- a/x/generators/shape_tagged.go +++ b/x/generators/shape_tagged.go @@ -347,6 +347,9 @@ func ShapeToString(x shape.Shape) string { func KindToGoName(kind shape.NumberKind) string { return shape.MatchNumberKindR1( kind, + func(x *shape.UInt) string { + return "shape.UInt{}" + }, func(x *shape.UInt8) string { return "shape.UInt8{}" }, @@ -359,6 +362,9 @@ func KindToGoName(kind shape.NumberKind) string { func(x *shape.UInt64) string { return "shape.UInt64{}" }, + func(x *shape.Int) string { + return "shape.Int{}" + }, func(x *shape.Int8) string { return "shape.Int8{}" }, diff --git a/x/schema/go.go b/x/schema/go.go index b34e8b59..ebf62f87 100644 --- a/x/schema/go.go +++ b/x/schema/go.go @@ -25,34 +25,34 @@ func FromPrimitiveGo(x any) Schema { return MkBool(y) case int: - return MkInt(y) + return MkInt(uint64(y)) case int8: - return MkInt(int(y)) + return MkInt(uint64(y)) case int16: - return MkInt(int(y)) + return MkInt(uint64(y)) case int32: - return MkInt(int(y)) + return MkInt(uint64(y)) case int64: - return MkInt(int(y)) + return MkInt(uint64(y)) case uint: - return MkInt(int(y)) + return MkInt(uint64(y)) case uint8: - return MkInt(int(y)) + return MkInt(uint64(y)) case uint16: - return MkInt(int(y)) + return MkInt(uint64(y)) case uint32: - return MkInt(int(y)) + return MkInt(uint64(y)) case uint64: - return MkInt(int(y)) + return MkInt(uint64(y)) case float32: return MkFloat(float64(y)) @@ -87,8 +87,8 @@ func FromPrimitiveGo(x any) Schema { return MkNone() } - if x, ok := x.(Schema); ok { - return x + if y, ok := x.(Schema); ok { + return y } panic(fmt.Errorf("schema.FromPrimitiveGo: unknown type %T", x)) @@ -262,36 +262,37 @@ func FromGoReflect(xschema shape.Shape, yreflect reflect.Value) Schema { return y } - // this is "int" type - if x.Kind == nil { - return MkInt(int(yreflect.Int())) - } - return shape.MatchNumberKindR1( x.Kind, + func(x *shape.UInt) Schema { + return MkInt(uint64(yreflect.Uint())) + }, func(x *shape.UInt8) Schema { - return MkInt(int(yreflect.Uint())) + return MkInt(uint64(yreflect.Uint())) }, func(x *shape.UInt16) Schema { - return MkInt(int(yreflect.Uint())) + return MkInt(uint64(yreflect.Uint())) }, func(x *shape.UInt32) Schema { - return MkInt(int(yreflect.Uint())) + return MkInt(uint64(yreflect.Uint())) }, func(x *shape.UInt64) Schema { - return MkInt(int(yreflect.Uint())) + return MkInt(uint64(yreflect.Uint())) + }, + func(x *shape.Int) Schema { + return MkInt(uint64(yreflect.Int())) }, func(x *shape.Int8) Schema { - return MkInt(int(yreflect.Int())) + return MkInt(uint64(yreflect.Int())) }, func(x *shape.Int16) Schema { - return MkInt(int(yreflect.Int())) + return MkInt(uint64(yreflect.Int())) }, func(x *shape.Int32) Schema { - return MkInt(int(yreflect.Int())) + return MkInt(uint64(yreflect.Int())) }, func(x *shape.Int64) Schema { - return MkInt(int(yreflect.Int())) + return MkInt(uint64(yreflect.Int())) }, func(x *shape.Float32) Schema { return MkFloat(yreflect.Float()) @@ -479,6 +480,9 @@ func ToGoReflect(xshape shape.Shape, ydata Schema, zreflect reflect.Type) (refle return shape.MatchNumberKindR2( x.Kind, + func(x *shape.UInt) (reflect.Value, error) { + return reflect.ValueOf(uint(*data)), nil + }, func(x *shape.UInt8) (reflect.Value, error) { return reflect.ValueOf(uint8(*data)), nil }, @@ -491,6 +495,9 @@ func ToGoReflect(xshape shape.Shape, ydata Schema, zreflect reflect.Type) (refle func(x *shape.UInt64) (reflect.Value, error) { return reflect.ValueOf(uint64(*data)), nil }, + func(x *shape.Int) (reflect.Value, error) { + return reflect.ValueOf(int(*data)), nil + }, func(x *shape.Int8) (reflect.Value, error) { return reflect.ValueOf(int8(*data)), nil }, @@ -630,8 +637,6 @@ func ToGoReflect(xshape shape.Shape, ydata Schema, zreflect reflect.Type) (refle return reflect.Value{}, fmt.Errorf("schema.ToGoReflect: shape.UnionLike expected reflect.Interface, got %T", zreflect) } - //TODO add $type field lookup for faster execution - for _, variant := range x.Variant { variantName := shape.ToGoTypeName(variant) _, found := (*data)[variantName] diff --git a/x/schema/location.go b/x/schema/location.go index 6f04ff0d..21c44b26 100644 --- a/x/schema/location.go +++ b/x/schema/location.go @@ -22,7 +22,8 @@ func LocationToStr(location []Location) string { result += MatchLocationR1( l, func(x *LocationField) string { - if strings.Contains(x.Name, ".") { + if strings.Contains(x.Name, ".") || + strings.Contains(x.Name, "$") { return fmt.Sprintf(`["%s"]`, x.Name) } return "." + x.Name diff --git a/x/schema/location_path_test.go b/x/schema/location_path_test.go index 21ec40c1..57a8ad97 100644 --- a/x/schema/location_path_test.go +++ b/x/schema/location_path_test.go @@ -37,3 +37,16 @@ func TestParseLocation2(t *testing.T) { }, result) assert.Equal(t, input, LocationToStr(result)) } + +func TestParseLocationSpecialCases(t *testing.T) { + input := `Tree["$type"].Value[*]` + result, err := ParseLocation(input) + assert.NoError(t, err) + assert.Equal(t, []Location{ + &LocationField{Name: "Tree"}, + &LocationField{Name: "$type"}, + &LocationField{Name: "Value"}, + &LocationAnything{}, + }, result) + assert.Equal(t, input, LocationToStr(result)) +} diff --git a/x/storage/schemaless/typedful/typed_location.go b/x/schema/location_typed.go similarity index 57% rename from x/storage/schemaless/typedful/typed_location.go rename to x/schema/location_typed.go index d2f35bc3..1c4620c0 100644 --- a/x/storage/schemaless/typedful/typed_location.go +++ b/x/schema/location_typed.go @@ -1,8 +1,7 @@ -package typedful +package schema import ( "fmt" - "github.com/widmogrod/mkunion/x/schema" "github.com/widmogrod/mkunion/x/shape" ) @@ -22,7 +21,7 @@ type TypedLocation struct { } func (location *TypedLocation) WrapLocationStr(field string) (string, error) { - loc, err := schema.ParseLocation(field) + loc, err := ParseLocation(field) if err != nil { return "", fmt.Errorf("typedful.WrapLocationStr: %w", err) } @@ -32,28 +31,28 @@ func (location *TypedLocation) WrapLocationStr(field string) (string, error) { return "", fmt.Errorf("typedful.WrapLocationStr: %w", err) } - return schema.LocationToStr(loc), nil + return LocationToStr(loc), nil } -func (location *TypedLocation) WrapLocation(loc []schema.Location) ([]schema.Location, error) { +func (location *TypedLocation) WrapLocation(loc []Location) ([]Location, error) { loc = location.wrapLocationShapeAware(loc, location.shape) return loc, nil } -func (location *TypedLocation) wrapLocationShapeAware(loc []schema.Location, s shape.Shape) []schema.Location { +func (location *TypedLocation) wrapLocationShapeAware(loc []Location, s shape.Shape) []Location { if len(loc) == 0 { return loc } - return schema.MatchLocationR1( + return MatchLocationR1( loc[0], - func(x *schema.LocationField) []schema.Location { + func(x *LocationField) []Location { return shape.MatchShapeR1( s, - func(y *shape.Any) []schema.Location { + func(y *shape.Any) []Location { panic("not implemented") }, - func(y *shape.RefName) []schema.Location { + func(y *shape.RefName) []Location { s, ok := shape.LookupShape(y) if !ok { panic(fmt.Errorf("wrapLocationShapeAware: shape.RefName not found %s; %w", y.Name, shape.ErrShapeNotFound)) @@ -61,27 +60,27 @@ func (location *TypedLocation) wrapLocationShapeAware(loc []schema.Location, s s return location.wrapLocationShapeAware(loc, s) }, - func(x *shape.PointerLike) []schema.Location { + func(x *shape.PointerLike) []Location { return location.wrapLocationShapeAware(loc, x.Type) }, - func(y *shape.AliasLike) []schema.Location { + func(y *shape.AliasLike) []Location { panic("not implemented") }, - func(x *shape.PrimitiveLike) []schema.Location { + func(x *shape.PrimitiveLike) []Location { panic("not implemented") }, - func(y *shape.ListLike) []schema.Location { + func(y *shape.ListLike) []Location { panic("not implemented") }, - func(y *shape.MapLike) []schema.Location { + func(y *shape.MapLike) []Location { panic("not implemented") }, - func(y *shape.StructLike) []schema.Location { + func(y *shape.StructLike) []Location { for _, field := range y.Fields { if field.Name == x.Name { result := location.wrapLocationShapeAware(loc[1:], field.Type) return append( - append([]schema.Location{x}, location.shapeToSchemaName(field.Type)...), + append([]Location{x}, location.shapeToSchemaName(field.Type)...), result..., ) } @@ -89,12 +88,18 @@ func (location *TypedLocation) wrapLocationShapeAware(loc []schema.Location, s s panic(fmt.Errorf("wrapLocationShapeAware: field %s not found in struct %s", x.Name, y.Name)) }, - func(y *shape.UnionLike) []schema.Location { + func(y *shape.UnionLike) []Location { + if x.Name == "$type" { + return append([]Location{x}, location.shapeToSchemaName(&shape.PrimitiveLike{ + Kind: &shape.StringLike{}, + })...) + } + for _, variant := range y.Variant { if shape.ToGoTypeName(variant) == x.Name { result := location.wrapLocationShapeAware(loc[1:], variant) return append( - append([]schema.Location{x}, location.shapeToSchemaName(variant)...), + append([]Location{x}, location.shapeToSchemaName(variant)...), result..., ) } @@ -104,76 +109,76 @@ func (location *TypedLocation) wrapLocationShapeAware(loc []schema.Location, s s }, ) }, - func(x *schema.LocationIndex) []schema.Location { + func(x *LocationIndex) []Location { panic("not implemented") }, - func(x *schema.LocationAnything) []schema.Location { + func(x *LocationAnything) []Location { panic("not implemented") }, ) } -func (location *TypedLocation) shapeToSchemaName(x shape.Shape) []schema.Location { +func (location *TypedLocation) shapeToSchemaName(x shape.Shape) []Location { return shape.MatchShapeR1( x, - func(x *shape.Any) []schema.Location { + func(x *shape.Any) []Location { panic("not implemented") }, - func(x *shape.RefName) []schema.Location { + func(x *shape.RefName) []Location { s, found := shape.LookupShape(x) if !found { panic(fmt.Errorf("shapeToSchemaName: shape.RefName not found %s; %w", x.Name, shape.ErrShapeNotFound)) } return location.shapeToSchemaName(s) }, - func(x *shape.PointerLike) []schema.Location { + func(x *shape.PointerLike) []Location { return location.shapeToSchemaName(x.Type) }, - func(x *shape.AliasLike) []schema.Location { + func(x *shape.AliasLike) []Location { panic("not implemented") }, - func(x *shape.PrimitiveLike) []schema.Location { + func(x *shape.PrimitiveLike) []Location { return shape.MatchPrimitiveKindR1( x.Kind, - func(x *shape.BooleanLike) []schema.Location { - return []schema.Location{ - &schema.LocationField{ + func(x *shape.BooleanLike) []Location { + return []Location{ + &LocationField{ Name: "schema.Boolean", }, } }, - func(x *shape.StringLike) []schema.Location { - return []schema.Location{ - &schema.LocationField{ + func(x *shape.StringLike) []Location { + return []Location{ + &LocationField{ Name: "schema.String", }, } }, - func(x *shape.NumberLike) []schema.Location { - return []schema.Location{ - &schema.LocationField{ + func(x *shape.NumberLike) []Location { + return []Location{ + &LocationField{ Name: "schema.Number", }, } }, ) }, - func(x *shape.ListLike) []schema.Location { + func(x *shape.ListLike) []Location { panic("not implemented") }, - func(x *shape.MapLike) []schema.Location { + func(x *shape.MapLike) []Location { panic("not implemented") }, - func(x *shape.StructLike) []schema.Location { - return []schema.Location{ - &schema.LocationField{ + func(x *shape.StructLike) []Location { + return []Location{ + &LocationField{ Name: "schema.Map", }, } }, - func(x *shape.UnionLike) []schema.Location { - return []schema.Location{ - &schema.LocationField{ + func(x *shape.UnionLike) []Location { + return []Location{ + &LocationField{ Name: "schema.Map", }, } diff --git a/x/schema/location_union_gen.go b/x/schema/location_union_gen.go index c181aef7..b711db36 100644 --- a/x/schema/location_union_gen.go +++ b/x/schema/location_union_gen.go @@ -144,7 +144,9 @@ func LocationIndexShape() shape.Shape { { Name: "Index", Type: &shape.PrimitiveLike{ - Kind: &shape.NumberLike{}, + Kind: &shape.NumberLike{ + Kind: &shape.Int{}, + }, }, }, }, diff --git a/x/schema/model.go b/x/schema/model.go index eb34f855..1fba3ff2 100644 --- a/x/schema/model.go +++ b/x/schema/model.go @@ -34,7 +34,7 @@ func MkBool(b bool) *Bool { return (*Bool)(&b) } -func MkInt(x int) *Number { +func MkInt(x uint64) *Number { v := float64(x) return (*Number)(&v) } diff --git a/x/schema/schema_test.go b/x/schema/schema_test.go deleted file mode 100644 index d7b23d5a..00000000 --- a/x/schema/schema_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package schema - -import ( - "github.com/stretchr/testify/assert" - "math" - "runtime" - "testing" -) - -type Max struct { - Int int - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - - Float32 float32 - Float64 float64 - - Uint uint - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 -} - -func TestMaxScalars(t *testing.T) { - max := Max{ - Int: math.MaxInt, - Int8: math.MaxInt8, - Int16: math.MaxInt16, - Int32: math.MaxInt32, - Int64: math.MaxInt64, - Float32: math.MaxFloat32, - Float64: math.MaxFloat64, - Uint: math.MaxUint, - Uint8: math.MaxInt8, - Uint16: math.MaxUint16, - Uint32: math.MaxUint32, - Uint64: math.MaxUint64, - } - - t.Run("max scalars for respective values contain correct value", func(t *testing.T) { - t.Skip("not implemented") - if runtime.GOARCH != "arm64" { - t.Skip("skipping test that are for ARM64") - } - - s := FromPrimitiveGo(max) - assert.Equal(t, &Map{ - "Int": MkInt(math.MaxInt), - "Int8": MkInt(math.MaxInt8), - "Int16": MkInt(math.MaxInt16), - "Int32": MkInt(math.MaxInt32), - "Int64": MkInt(math.MaxInt64), - "Float32": MkFloat(math.MaxFloat32), - "Float64": MkFloat(math.MaxFloat64), - //"Uint": MkInt(math.MaxUint), - "Uint8": MkInt(math.MaxInt8), - "Uint16": MkInt(math.MaxUint16), - "Uint32": MkInt(math.MaxUint32), - //"Uint64": MkInt(math.MaxUint64), - }, s) - }) -} diff --git a/x/schema/testutil/max.go b/x/schema/testutil/max.go new file mode 100644 index 00000000..940368dc --- /dev/null +++ b/x/schema/testutil/max.go @@ -0,0 +1,21 @@ +package testutil + +//go:generate go run ../../../cmd/mkunion/main.go serde + +//go:tag serde:"json" +type Max struct { + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + + Float32 float32 + Float64 float64 + + Uint uint + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 +} diff --git a/x/schema/testutil/max_test.go b/x/schema/testutil/max_test.go new file mode 100644 index 00000000..035fecff --- /dev/null +++ b/x/schema/testutil/max_test.go @@ -0,0 +1,49 @@ +package testutil + +import ( + "github.com/stretchr/testify/assert" + "github.com/widmogrod/mkunion/x/schema" + "math" + "runtime" + "testing" +) + +func TestMaxScalars(t *testing.T) { + max := Max{ + Int: math.MaxInt, + Int8: math.MaxInt8, + Int16: math.MaxInt16, + Int32: math.MaxInt32, + Int64: math.MaxInt64, + Float32: math.MaxFloat32, + Float64: math.MaxFloat64, + Uint: math.MaxUint, + Uint8: math.MaxInt8, + Uint16: math.MaxUint16, + Uint32: math.MaxUint32, + Uint64: math.MaxUint64, + } + + t.Run("max scalars for respective values contain correct value", func(t *testing.T) { + //t.Skip("not implemented") + if runtime.GOARCH != "arm64" { + t.Skip("skipping test that are for ARM64") + } + + s := schema.FromGo(max) + assert.Equal(t, &schema.Map{ + "Int": schema.MkInt(math.MaxInt), + "Int8": schema.MkInt(math.MaxInt8), + "Int16": schema.MkInt(math.MaxInt16), + "Int32": schema.MkInt(math.MaxInt32), + "Int64": schema.MkInt(math.MaxInt64), + "Float32": schema.MkFloat(math.MaxFloat32), + "Float64": schema.MkFloat(math.MaxFloat64), + "Uint": schema.MkInt(math.MaxUint), + "Uint8": schema.MkInt(math.MaxInt8), + "Uint16": schema.MkInt(math.MaxUint16), + "Uint32": schema.MkInt(math.MaxUint32), + "Uint64": schema.MkInt(math.MaxUint64), + }, s) + }) +} diff --git a/x/schema/utils.go b/x/schema/utils.go index 0c1a2aeb..d4d3d858 100644 --- a/x/schema/utils.go +++ b/x/schema/utils.go @@ -315,11 +315,21 @@ func GetShapeSchemaLocation(s shape.Shape, data Schema, locations []Location) (S return nil } - _, ok = (*mapData)[x.Name] + value, ok := (*mapData)[x.Name] if !ok { return nil } + if x.Name == "$type" { + return &locres{ + data: value, + loc: locations, + shape: &shape.PrimitiveLike{ + Kind: &shape.StringLike{}, + }, + } + } + for _, variant := range y.Variant { fieldName := shape.ToGoTypeName(variant) if x.Name != fieldName { diff --git a/x/shape/fromfile_test.go b/x/shape/fromfile_test.go index 07897890..9da2ff63 100644 --- a/x/shape/fromfile_test.go +++ b/x/shape/fromfile_test.go @@ -49,8 +49,10 @@ func TestInferFromFile(t *testing.T) { PkgImportName: "github.com/widmogrod/mkunion/x/shape/testasset", Fields: []*FieldLike{ { - Name: "Age", - Type: &PrimitiveLike{Kind: &NumberLike{}}, + Name: "Age", + Type: &PrimitiveLike{Kind: &NumberLike{ + Kind: &Int{}, + }}, Desc: nil, Guard: nil, Tags: map[string]Tag{ diff --git a/x/shape/shape.go b/x/shape/shape.go index 12b6731c..1dd76706 100644 --- a/x/shape/shape.go +++ b/x/shape/shape.go @@ -79,10 +79,12 @@ type TypeParam struct { //go:tag mkunion:"NumberKind" type ( + UInt struct{} UInt8 struct{} UInt16 struct{} UInt32 struct{} UInt64 struct{} + Int struct{} Int8 struct{} Int16 struct{} Int32 struct{} @@ -92,10 +94,12 @@ type ( ) var TypeStringToNumberKindMap = map[string]NumberKind{ + "uint": &UInt{}, "uint8": &UInt8{}, "uint16": &UInt16{}, "uint32": &UInt32{}, "uint64": &UInt64{}, + "int": &Int{}, "int8": &Int8{}, "int16": &Int16{}, "int32": &Int32{}, @@ -127,12 +131,11 @@ func IsBinary(x Shape) bool { } func NumberKindToGoName(x NumberKind) string { - if x == nil { - return "int" - } - return MatchNumberKindR1( x, + func(x *UInt) string { + return "uint" + }, func(x *UInt8) string { return "uint8" }, @@ -145,6 +148,9 @@ func NumberKindToGoName(x NumberKind) string { func(x *UInt64) string { return "uint64" }, + func(x *Int) string { + return "int" + }, func(x *Int8) string { return "int8" }, diff --git a/x/shape/shape_union_gen.go b/x/shape/shape_union_gen.go index 6143d252..0d898f2d 100644 --- a/x/shape/shape_union_gen.go +++ b/x/shape/shape_union_gen.go @@ -23,10 +23,12 @@ func init() { Register(StringLikeShape()) Register(NumberLikeShape()) Register(NumberKindShape()) + Register(UIntShape()) Register(UInt8Shape()) Register(UInt16Shape()) Register(UInt32Shape()) Register(UInt64Shape()) + Register(IntShape()) Register(Int8Shape()) Register(Int16Shape()) Register(Int32Shape()) @@ -2428,10 +2430,12 @@ func (r *NumberLike) _unmarshalJSONNumberKind(data []byte) (NumberKind, error) { } type NumberKindVisitor interface { + VisitUInt(v *UInt) any VisitUInt8(v *UInt8) any VisitUInt16(v *UInt16) any VisitUInt32(v *UInt32) any VisitUInt64(v *UInt64) any + VisitInt(v *Int) any VisitInt8(v *Int8) any VisitInt16(v *Int16) any VisitInt32(v *Int32) any @@ -2445,10 +2449,12 @@ type NumberKind interface { } var ( + _ NumberKind = (*UInt)(nil) _ NumberKind = (*UInt8)(nil) _ NumberKind = (*UInt16)(nil) _ NumberKind = (*UInt32)(nil) _ NumberKind = (*UInt64)(nil) + _ NumberKind = (*Int)(nil) _ NumberKind = (*Int8)(nil) _ NumberKind = (*Int16)(nil) _ NumberKind = (*Int32)(nil) @@ -2457,10 +2463,12 @@ var ( _ NumberKind = (*Float64)(nil) ) +func (r *UInt) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitUInt(r) } func (r *UInt8) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitUInt8(r) } func (r *UInt16) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitUInt16(r) } func (r *UInt32) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitUInt32(r) } func (r *UInt64) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitUInt64(r) } +func (r *Int) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitInt(r) } func (r *Int8) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitInt8(r) } func (r *Int16) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitInt16(r) } func (r *Int32) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitInt32(r) } @@ -2470,38 +2478,44 @@ func (r *Float64) AcceptNumberKind(v NumberKindVisitor) any { return v.VisitFloa func MatchNumberKindR3[T0, T1, T2 any]( x NumberKind, - f1 func(x *UInt8) (T0, T1, T2), - f2 func(x *UInt16) (T0, T1, T2), - f3 func(x *UInt32) (T0, T1, T2), - f4 func(x *UInt64) (T0, T1, T2), - f5 func(x *Int8) (T0, T1, T2), - f6 func(x *Int16) (T0, T1, T2), - f7 func(x *Int32) (T0, T1, T2), - f8 func(x *Int64) (T0, T1, T2), - f9 func(x *Float32) (T0, T1, T2), - f10 func(x *Float64) (T0, T1, T2), + f1 func(x *UInt) (T0, T1, T2), + f2 func(x *UInt8) (T0, T1, T2), + f3 func(x *UInt16) (T0, T1, T2), + f4 func(x *UInt32) (T0, T1, T2), + f5 func(x *UInt64) (T0, T1, T2), + f6 func(x *Int) (T0, T1, T2), + f7 func(x *Int8) (T0, T1, T2), + f8 func(x *Int16) (T0, T1, T2), + f9 func(x *Int32) (T0, T1, T2), + f10 func(x *Int64) (T0, T1, T2), + f11 func(x *Float32) (T0, T1, T2), + f12 func(x *Float64) (T0, T1, T2), ) (T0, T1, T2) { switch v := x.(type) { - case *UInt8: + case *UInt: return f1(v) - case *UInt16: + case *UInt8: return f2(v) - case *UInt32: + case *UInt16: return f3(v) - case *UInt64: + case *UInt32: return f4(v) - case *Int8: + case *UInt64: return f5(v) - case *Int16: + case *Int: return f6(v) - case *Int32: + case *Int8: return f7(v) - case *Int64: + case *Int16: return f8(v) - case *Float32: + case *Int32: return f9(v) - case *Float64: + case *Int64: return f10(v) + case *Float32: + return f11(v) + case *Float64: + return f12(v) } var result1 T0 var result2 T1 @@ -2511,38 +2525,44 @@ func MatchNumberKindR3[T0, T1, T2 any]( func MatchNumberKindR2[T0, T1 any]( x NumberKind, - f1 func(x *UInt8) (T0, T1), - f2 func(x *UInt16) (T0, T1), - f3 func(x *UInt32) (T0, T1), - f4 func(x *UInt64) (T0, T1), - f5 func(x *Int8) (T0, T1), - f6 func(x *Int16) (T0, T1), - f7 func(x *Int32) (T0, T1), - f8 func(x *Int64) (T0, T1), - f9 func(x *Float32) (T0, T1), - f10 func(x *Float64) (T0, T1), + f1 func(x *UInt) (T0, T1), + f2 func(x *UInt8) (T0, T1), + f3 func(x *UInt16) (T0, T1), + f4 func(x *UInt32) (T0, T1), + f5 func(x *UInt64) (T0, T1), + f6 func(x *Int) (T0, T1), + f7 func(x *Int8) (T0, T1), + f8 func(x *Int16) (T0, T1), + f9 func(x *Int32) (T0, T1), + f10 func(x *Int64) (T0, T1), + f11 func(x *Float32) (T0, T1), + f12 func(x *Float64) (T0, T1), ) (T0, T1) { switch v := x.(type) { - case *UInt8: + case *UInt: return f1(v) - case *UInt16: + case *UInt8: return f2(v) - case *UInt32: + case *UInt16: return f3(v) - case *UInt64: + case *UInt32: return f4(v) - case *Int8: + case *UInt64: return f5(v) - case *Int16: + case *Int: return f6(v) - case *Int32: + case *Int8: return f7(v) - case *Int64: + case *Int16: return f8(v) - case *Float32: + case *Int32: return f9(v) - case *Float64: + case *Int64: return f10(v) + case *Float32: + return f11(v) + case *Float64: + return f12(v) } var result1 T0 var result2 T1 @@ -2551,38 +2571,44 @@ func MatchNumberKindR2[T0, T1 any]( func MatchNumberKindR1[T0 any]( x NumberKind, - f1 func(x *UInt8) T0, - f2 func(x *UInt16) T0, - f3 func(x *UInt32) T0, - f4 func(x *UInt64) T0, - f5 func(x *Int8) T0, - f6 func(x *Int16) T0, - f7 func(x *Int32) T0, - f8 func(x *Int64) T0, - f9 func(x *Float32) T0, - f10 func(x *Float64) T0, + f1 func(x *UInt) T0, + f2 func(x *UInt8) T0, + f3 func(x *UInt16) T0, + f4 func(x *UInt32) T0, + f5 func(x *UInt64) T0, + f6 func(x *Int) T0, + f7 func(x *Int8) T0, + f8 func(x *Int16) T0, + f9 func(x *Int32) T0, + f10 func(x *Int64) T0, + f11 func(x *Float32) T0, + f12 func(x *Float64) T0, ) T0 { switch v := x.(type) { - case *UInt8: + case *UInt: return f1(v) - case *UInt16: + case *UInt8: return f2(v) - case *UInt32: + case *UInt16: return f3(v) - case *UInt64: + case *UInt32: return f4(v) - case *Int8: + case *UInt64: return f5(v) - case *Int16: + case *Int: return f6(v) - case *Int32: + case *Int8: return f7(v) - case *Int64: + case *Int16: return f8(v) - case *Float32: + case *Int32: return f9(v) - case *Float64: + case *Int64: return f10(v) + case *Float32: + return f11(v) + case *Float64: + return f12(v) } var result1 T0 return result1 @@ -2590,38 +2616,44 @@ func MatchNumberKindR1[T0 any]( func MatchNumberKindR0( x NumberKind, - f1 func(x *UInt8), - f2 func(x *UInt16), - f3 func(x *UInt32), - f4 func(x *UInt64), - f5 func(x *Int8), - f6 func(x *Int16), - f7 func(x *Int32), - f8 func(x *Int64), - f9 func(x *Float32), - f10 func(x *Float64), + f1 func(x *UInt), + f2 func(x *UInt8), + f3 func(x *UInt16), + f4 func(x *UInt32), + f5 func(x *UInt64), + f6 func(x *Int), + f7 func(x *Int8), + f8 func(x *Int16), + f9 func(x *Int32), + f10 func(x *Int64), + f11 func(x *Float32), + f12 func(x *Float64), ) { switch v := x.(type) { - case *UInt8: + case *UInt: f1(v) - case *UInt16: + case *UInt8: f2(v) - case *UInt32: + case *UInt16: f3(v) - case *UInt64: + case *UInt32: f4(v) - case *Int8: + case *UInt64: f5(v) - case *Int16: + case *Int: f6(v) - case *Int32: + case *Int8: f7(v) - case *Int64: + case *Int16: f8(v) - case *Float32: + case *Int32: f9(v) - case *Float64: + case *Int64: f10(v) + case *Float32: + f11(v) + case *Float64: + f12(v) } } @@ -2631,10 +2663,12 @@ func NumberKindShape() Shape { PkgName: "shape", PkgImportName: "github.com/widmogrod/mkunion/x/shape", Variant: []Shape{ + UIntShape(), UInt8Shape(), UInt16Shape(), UInt32Shape(), UInt64Shape(), + IntShape(), Int8Shape(), Int16Shape(), Int32Shape(), @@ -2645,6 +2679,14 @@ func NumberKindShape() Shape { } } +func UIntShape() Shape { + return &StructLike{ + Name: "UInt", + PkgName: "shape", + PkgImportName: "github.com/widmogrod/mkunion/x/shape", + } +} + func UInt8Shape() Shape { return &StructLike{ Name: "UInt8", @@ -2677,6 +2719,14 @@ func UInt64Shape() Shape { } } +func IntShape() Shape { + return &StructLike{ + Name: "Int", + PkgName: "shape", + PkgImportName: "github.com/widmogrod/mkunion/x/shape", + } +} + func Int8Shape() Shape { return &StructLike{ Name: "Int8", @@ -2726,10 +2776,12 @@ func Float64Shape() Shape { } func init() { shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.NumberKind", NumberKindFromJSON, NumberKindToJSON) + shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.UInt", UIntFromJSON, UIntToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.UInt8", UInt8FromJSON, UInt8ToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.UInt16", UInt16FromJSON, UInt16ToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.UInt32", UInt32FromJSON, UInt32ToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.UInt64", UInt64FromJSON, UInt64ToJSON) + shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.Int", IntFromJSON, IntToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.Int8", Int8FromJSON, Int8ToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.Int16", Int16FromJSON, Int16ToJSON) shared.JSONMarshallerRegister("github.com/widmogrod/mkunion/x/shape.Int32", Int32FromJSON, Int32ToJSON) @@ -2740,10 +2792,12 @@ func init() { type NumberKindUnionJSON struct { Type string `json:"$type,omitempty"` + UInt json.RawMessage `json:"shape.UInt,omitempty"` UInt8 json.RawMessage `json:"shape.UInt8,omitempty"` UInt16 json.RawMessage `json:"shape.UInt16,omitempty"` UInt32 json.RawMessage `json:"shape.UInt32,omitempty"` UInt64 json.RawMessage `json:"shape.UInt64,omitempty"` + Int json.RawMessage `json:"shape.Int,omitempty"` Int8 json.RawMessage `json:"shape.Int8,omitempty"` Int16 json.RawMessage `json:"shape.Int16,omitempty"` Int32 json.RawMessage `json:"shape.Int32,omitempty"` @@ -2767,6 +2821,8 @@ func NumberKindFromJSON(x []byte) (NumberKind, error) { } switch data.Type { + case "shape.UInt": + return UIntFromJSON(data.UInt) case "shape.UInt8": return UInt8FromJSON(data.UInt8) case "shape.UInt16": @@ -2775,6 +2831,8 @@ func NumberKindFromJSON(x []byte) (NumberKind, error) { return UInt32FromJSON(data.UInt32) case "shape.UInt64": return UInt64FromJSON(data.UInt64) + case "shape.Int": + return IntFromJSON(data.Int) case "shape.Int8": return Int8FromJSON(data.Int8) case "shape.Int16": @@ -2789,7 +2847,9 @@ func NumberKindFromJSON(x []byte) (NumberKind, error) { return Float64FromJSON(data.Float64) } - if data.UInt8 != nil { + if data.UInt != nil { + return UIntFromJSON(data.UInt) + } else if data.UInt8 != nil { return UInt8FromJSON(data.UInt8) } else if data.UInt16 != nil { return UInt16FromJSON(data.UInt16) @@ -2797,6 +2857,8 @@ func NumberKindFromJSON(x []byte) (NumberKind, error) { return UInt32FromJSON(data.UInt32) } else if data.UInt64 != nil { return UInt64FromJSON(data.UInt64) + } else if data.Int != nil { + return IntFromJSON(data.Int) } else if data.Int8 != nil { return Int8FromJSON(data.Int8) } else if data.Int16 != nil { @@ -2820,6 +2882,17 @@ func NumberKindToJSON(x NumberKind) ([]byte, error) { } return MatchNumberKindR2( x, + func(x *UInt) ([]byte, error) { + body, err := UIntToJSON(x) + if err != nil { + return nil, err + } + + return json.Marshal(NumberKindUnionJSON{ + Type: "shape.UInt", + UInt: body, + }) + }, func(x *UInt8) ([]byte, error) { body, err := UInt8ToJSON(x) if err != nil { @@ -2864,6 +2937,17 @@ func NumberKindToJSON(x NumberKind) ([]byte, error) { UInt64: body, }) }, + func(x *Int) ([]byte, error) { + body, err := IntToJSON(x) + if err != nil { + return nil, err + } + + return json.Marshal(NumberKindUnionJSON{ + Type: "shape.Int", + Int: body, + }) + }, func(x *Int8) ([]byte, error) { body, err := Int8ToJSON(x) if err != nil { @@ -2933,6 +3017,58 @@ func NumberKindToJSON(x NumberKind) ([]byte, error) { ) } +func UIntFromJSON(x []byte) (*UInt, error) { + result := new(UInt) + err := result.UnmarshalJSON(x) + if err != nil { + return nil, err + } + + return result, nil +} + +func UIntToJSON(x *UInt) ([]byte, error) { + return x.MarshalJSON() +} + +var ( + _ json.Unmarshaler = (*UInt)(nil) + _ json.Marshaler = (*UInt)(nil) +) + +func (r *UInt) MarshalJSON() ([]byte, error) { + if r == nil { + return nil, nil + } + return r._marshalJSONUInt(*r) +} +func (r *UInt) _marshalJSONUInt(x UInt) ([]byte, error) { + partial := make(map[string]json.RawMessage) + var err error + result, err := json.Marshal(partial) + if err != nil { + return nil, fmt.Errorf("shape: UInt._marshalJSONUInt: struct; %w", err) + } + return result, nil +} +func (r *UInt) UnmarshalJSON(data []byte) error { + result, err := r._unmarshalJSONUInt(data) + if err != nil { + return fmt.Errorf("shape: UInt.UnmarshalJSON: %w", err) + } + *r = result + return nil +} +func (r *UInt) _unmarshalJSONUInt(data []byte) (UInt, error) { + result := UInt{} + var partial map[string]json.RawMessage + err := json.Unmarshal(data, &partial) + if err != nil { + return result, fmt.Errorf("shape: UInt._unmarshalJSONUInt: native struct unwrap; %w", err) + } + return result, nil +} + func UInt8FromJSON(x []byte) (*UInt8, error) { result := new(UInt8) err := result.UnmarshalJSON(x) @@ -3141,6 +3277,58 @@ func (r *UInt64) _unmarshalJSONUInt64(data []byte) (UInt64, error) { return result, nil } +func IntFromJSON(x []byte) (*Int, error) { + result := new(Int) + err := result.UnmarshalJSON(x) + if err != nil { + return nil, err + } + + return result, nil +} + +func IntToJSON(x *Int) ([]byte, error) { + return x.MarshalJSON() +} + +var ( + _ json.Unmarshaler = (*Int)(nil) + _ json.Marshaler = (*Int)(nil) +) + +func (r *Int) MarshalJSON() ([]byte, error) { + if r == nil { + return nil, nil + } + return r._marshalJSONInt(*r) +} +func (r *Int) _marshalJSONInt(x Int) ([]byte, error) { + partial := make(map[string]json.RawMessage) + var err error + result, err := json.Marshal(partial) + if err != nil { + return nil, fmt.Errorf("shape: Int._marshalJSONInt: struct; %w", err) + } + return result, nil +} +func (r *Int) UnmarshalJSON(data []byte) error { + result, err := r._unmarshalJSONInt(data) + if err != nil { + return fmt.Errorf("shape: Int.UnmarshalJSON: %w", err) + } + *r = result + return nil +} +func (r *Int) _unmarshalJSONInt(data []byte) (Int, error) { + result := Int{} + var partial map[string]json.RawMessage + err := json.Unmarshal(data, &partial) + if err != nil { + return result, fmt.Errorf("shape: Int._unmarshalJSONInt: native struct unwrap; %w", err) + } + return result, nil +} + func Int8FromJSON(x []byte) (*Int8, error) { result := new(Int8) err := result.UnmarshalJSON(x) diff --git a/x/storage/predicate/evaluate.go b/x/storage/predicate/evaluate.go index 7e23f95f..2c5d6709 100644 --- a/x/storage/predicate/evaluate.go +++ b/x/storage/predicate/evaluate.go @@ -59,7 +59,7 @@ func EvaluateShape(predicate Predicate, s shape.Shape, data schema.Schema, bind fieldValue, _ := schema.GetShapeLocation(s, data, x.Location) cmp := schema.Compare(fieldValue, value) switch x.Operation { - case "=": + case "=", "==": return cmp == 0 case "<": return cmp < 0 @@ -69,7 +69,7 @@ func EvaluateShape(predicate Predicate, s shape.Shape, data schema.Schema, bind return cmp <= 0 case ">=": return cmp >= 0 - case "<>": + case "<>", "!=": return cmp != 0 default: return false @@ -111,7 +111,7 @@ func EvaluateSchema(predicate Predicate, data schema.Schema, bind ParamBinds) bo fieldValue := schema.GetSchema(data, x.Location) cmp := schema.Compare(fieldValue, value) switch x.Operation { - case "=": + case "=", "==": return cmp == 0 case "<": return cmp < 0 @@ -121,7 +121,7 @@ func EvaluateSchema(predicate Predicate, data schema.Schema, bind ParamBinds) bo return cmp <= 0 case ">=": return cmp >= 0 - case "<>": + case "<>", "!=": return cmp != 0 default: return false diff --git a/x/storage/predicate/evaluate_test.go b/x/storage/predicate/evaluate_test.go index 7bb2383d..22f45b0d 100644 --- a/x/storage/predicate/evaluate_test.go +++ b/x/storage/predicate/evaluate_test.go @@ -80,6 +80,12 @@ func TestEvaluate(t *testing.T) { bind: defBind, result: true, }, + { + value: `Tree["$type"] == "testutil.Branch"`, + data: defValue, + bind: defBind, + result: true, + }, { value: `Tree[*].Right[*].Value["schema.Number"] = :leaf0val`, data: defValue, @@ -110,6 +116,24 @@ func TestEvaluate(t *testing.T) { bind: defBind, result: true, }, + { + value: `Age == 20`, + data: defValue, + bind: defBind, + result: true, + }, + { + value: `Age != 20`, + data: defValue, + bind: defBind, + result: false, + }, + { + value: `Age <> 20`, + data: defValue, + bind: defBind, + result: false, + }, { value: `Visible = true`, data: defValue, diff --git a/x/storage/predicate/parser.go b/x/storage/predicate/parser.go index 7b42dbca..c4087e58 100644 --- a/x/storage/predicate/parser.go +++ b/x/storage/predicate/parser.go @@ -11,9 +11,9 @@ var ( predicateLexer = lexer.MustSimple([]lexer.SimpleRule{ {"Whitespace", `\s+`}, {"Keyword", `AND|OR|NOT`}, - {"Operator", `(<>|<=|>=|=|<|>|!=|#=)`}, + {"Operator", `[\<\>\!\=]+`}, {"Bind", `:[a-zA-Z][a-zA-Z0-9]*`}, - {"Location", `[a-zA-Z][a-zA-Z0-9\#\.\[\]'"\*]*`}, + {"Location", `[a-zA-Z][a-zA-Z0-9\$\.\[\]'"\*]*`}, {"Number", `[-+]?[0-9]*\.?[0-9]+`}, {"String", `"[^"]+"`}, }) @@ -38,7 +38,7 @@ func Parse(input string) (Predicate, error) { type Comparable struct { Location string `( @Location` - Operator string ` @( "<>" | "<=" | ">=" | "=" | "<" | ">" | "!=" )` + Operator string ` @Operator` BindName Value ` @@)` } diff --git a/x/storage/schemaless/schema.go b/x/storage/schemaless/schema.go index 57c71d80..910a858c 100644 --- a/x/storage/schemaless/schema.go +++ b/x/storage/schemaless/schema.go @@ -147,11 +147,24 @@ func (s *InMemoryRepository[A]) FindingRecords(query FindingRecords[Record[A]]) records = sortRecords(records, query.Sort) } + // Use limit to reduce number of records + var next, prev *FindingRecords[Record[A]] + + // given list + // 1,2,3,4,5,6,7 + // find limit=4 + // 1,2,3,4 {next: 4, prev: nil} + // find limit=4 after=4 + // 5,6,7 {next: nil, prev: 4} + // find limit=4 before=4 + // 1,2,3 {next: 4, prev: nil} + if query.After != nil { found := false newRecords := make([]Record[A], 0) + positionID := *query.After for _, record := range records { - if predicate.EvaluateEqual[Record[A]](record, "ID", *query.After) { + if record.ID == positionID { found = true continue // we're interested in records after this one } @@ -160,19 +173,66 @@ func (s *InMemoryRepository[A]) FindingRecords(query FindingRecords[Record[A]]) } } records = newRecords - } - // Use limit to reduce number of records - var next *FindingRecords[Record[A]] - if query.Limit > 0 { - if len(records) > int(query.Limit) { - records = records[:query.Limit] - - next = &FindingRecords[Record[A]]{ - Where: query.Where, - Sort: query.Sort, - Limit: query.Limit, - After: &records[len(records)-1].ID, + prev = &FindingRecords[Record[A]]{ + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + Before: &positionID, + } + + if query.Limit > 0 { + if len(records) > int(query.Limit) { + records = records[:query.Limit] + next = &FindingRecords[Record[A]]{ + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + After: &records[len(records)-1].ID, + } + } + } + + } else if query.Before != nil { + newRecords := make([]Record[A], 0) + positionID := *query.Before + for _, record := range records { + newRecords = append(newRecords, record) + if record.ID == positionID { + break + } + } + records = newRecords + + next = &FindingRecords[Record[A]]{ + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + After: &positionID, + } + + if query.Limit > 0 { + if len(records) > int(query.Limit) { + records = records[len(records)-int(query.Limit):] + + prev = &FindingRecords[Record[A]]{ + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + Before: &records[0].ID, + } + } + } + } else { + if query.Limit > 0 { + if len(records) > int(query.Limit) { + records = records[:query.Limit] + next = &FindingRecords[Record[A]]{ + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + After: &records[len(records)-1].ID, + } } } } @@ -180,6 +240,7 @@ func (s *InMemoryRepository[A]) FindingRecords(query FindingRecords[Record[A]]) result := PageResult[Record[A]]{ Items: records, Next: next, + Prev: prev, } return result, nil diff --git a/x/storage/schemaless/schema_test.go b/x/storage/schemaless/schema_test.go index d6bf9047..d5ca9ab2 100644 --- a/x/storage/schemaless/schema_test.go +++ b/x/storage/schemaless/schema_test.go @@ -43,15 +43,15 @@ func TestNewRepository2WithSchema(t *testing.T) { if assert.Len(t, nextResult.Items, 1, "second page should have 1 item") { assert.Equal(t, "Zarlie", nextResult.Items[0].Data.Name, "no-name") - //// find last before - //if assert.True(t, nextResult.HasPrev(), "should have previous page of results") { - // beforeResult, err := repo.FindingRecords(*nextResult.Prev) - // assert.NoError(t, err) - // - // if assert.Len(t, beforeResult.Items, 1, "before page should have 1 item") { - // assert.Equal(t, "Jane", schema.As[string](schema.GetSchema(beforeResult.Items[0].Data, "Name"), "no-name")) - // } - //} + // find last before + if assert.True(t, nextResult.HasPrev(), "should have previous page of results") { + beforeResult, err := repo.FindingRecords(*nextResult.Prev) + assert.NoError(t, err) + + if assert.Len(t, beforeResult.Items, 2, "before page should have 1 item") { + assert.Equal(t, "Jane", beforeResult.Items[1].Data.Name, "no-name") + } + } } } } diff --git a/x/storage/schemaless/storage.go b/x/storage/schemaless/storage.go index 31172f5c..f7097da0 100644 --- a/x/storage/schemaless/storage.go +++ b/x/storage/schemaless/storage.go @@ -36,59 +36,6 @@ type Record[A any] struct { Version uint16 } -// func (r *Record[A]) MarshalJSON() ([]byte, error) { -// result := make(map[string]json.RawMessage) -// -// field_ID, err := json.Marshal(r.ID) -// if err != nil { -// return nil, err -// } -// result["ID"] = field_ID -// -// field_Type, err := json.Marshal(r.Type) -// if err != nil { -// return nil, err -// } -// result["Type"] = field_Type -// -// field_Data, err := shared.JSONMarshal[A](r.Data) -// if err != nil { -// return nil, err -// } -// result["Data"] = field_Data -// -// field_Version, err := json.Marshal(r.Version) -// if err != nil { -// return nil, err -// } -// result["Version"] = field_Version -// -// return json.Marshal(result) -// } -// -// func (r *Record[A]) UnmarshalJSON(bytes []byte) error { -// return shared.JSONParseObject(bytes, func(key string, bytes []byte) error { -// switch key { -// case "ID": -// return json.Unmarshal(bytes, &r.ID) -// case "Type": -// return json.Unmarshal(bytes, &r.Type) -// case "Data": -// return shared.JSONUnmarshal[A](bytes, &r.Data) -// case "Version": -// return json.Unmarshal(bytes, &r.Version) -// } -// -// return fmt.Errorf("schemaless.Record[A].UnmarshalJSON: unknown key: %s", key) -// }) -// } -// -// var ( -// -// _ json.Unmarshaler = (*Record[any])(nil) -// _ json.Marshaler = (*Record[any])(nil) -// -// ) type UpdatingPolicy uint const ( @@ -96,121 +43,46 @@ const ( PolicyOverwriteServerChanges ) -type ( - UpdateRecords[T any] struct { - UpdatingPolicy UpdatingPolicy - Saving map[string]T - Deleting map[string]T - } - FindingRecords[T any] struct { - RecordType string - Where *predicate.WherePredicates - Sort []SortField - Limit uint8 - After *Cursor - //Before *Cursor - } -) +type UpdateRecords[T any] struct { + UpdatingPolicy UpdatingPolicy + Saving map[string]T + Deleting map[string]T +} -func (s UpdateRecords[T]) IsEmpty() bool { - return len(s.Saving) == 0 && len(s.Deleting) == 0 +type FindingRecords[T any] struct { + RecordType string + Where *predicate.WherePredicates + Sort []SortField + Limit uint8 + After *Cursor + Before *Cursor } -type ( - //go:tag serde:"json" - SortField struct { - Field string - Descending bool - } +func (s *UpdateRecords[T]) IsEmpty() bool { + return len(s.Saving) == 0 && len(s.Deleting) == 0 +} - Cursor = string +//go:tag serde:"json" +type SortField struct { + Field string + Descending bool +} - //go:tag serde:"json" - PageResult[A any] struct { - Items []A - Next *FindingRecords[A] - } -) +type Cursor = string -//func (a *PageResult[A]) MarshalJSON() ([]byte, error) { -// result := map[string]json.RawMessage{} -// -// var field_Items []json.RawMessage -// for _, item := range a.Items { -// bytes, err := shared.JSONMarshal[A](item) -// if err != nil { -// return nil, err -// } -// -// field_Items = append(field_Items, bytes) -// } -// -// filed_ItemsS, err := json.Marshal(field_Items) -// if err != nil { -// return nil, err -// } -// -// result["Items"] = filed_ItemsS -// -// if a.Next != nil { -// bytes, err := shared.JSONMarshal[*FindingRecords[A]](a.Next) -// if err != nil { -// return nil, err -// } -// -// result["Next"] = bytes -// } -// -// return json.Marshal(result) -//} -// -//func (a *PageResult[A]) UnmarshalJSON(bytes []byte) error { -// return shared.JSONParseObject(bytes, func(key string, bytes []byte) error { -// switch key { -// case "Items": -// var inter []json.RawMessage -// err := json.Unmarshal(bytes, &inter) -// if err != nil { -// return err -// } -// -// var items []A -// for _, raw := range inter { -// var item *A = new(A) -// err := shared.JSONUnmarshal[A](raw, item) -// if err != nil { -// return err -// } -// -// items = append(items, *item) -// } -// -// a.Items = items -// return nil -// -// case "Next": -// var next *FindingRecords[A] = new(FindingRecords[A]) -// err := shared.JSONUnmarshal[*FindingRecords[A]](bytes, next) -// if err != nil { -// return err -// } -// -// a.Next = next -// return nil -// } -// -// return fmt.Errorf("schemaless.PageResult[A].UnmarshalJSON: unknown key: %s", key) -// }) -//} -// -//var ( -// _ json.Unmarshaler = (*PageResult[any])(nil) -// _ json.Marshaler = (*PageResult[any])(nil) -//) +//go:tag serde:"json" +type PageResult[A any] struct { + Items []A + Next *FindingRecords[A] + Prev *FindingRecords[A] +} -func (a PageResult[A]) HasNext() bool { +func (a *PageResult[A]) HasNext() bool { return a.Next != nil } +func (a *PageResult[A]) HasPrev() bool { + return a.Prev != nil +} type Storage[T any] interface { GetAs(id string, x *T) error diff --git a/x/storage/schemaless/storage_serde_gen.go b/x/storage/schemaless/storage_serde_gen.go index 84efb5ca..5dad522c 100644 --- a/x/storage/schemaless/storage_serde_gen.go +++ b/x/storage/schemaless/storage_serde_gen.go @@ -156,6 +156,14 @@ func (r *PageResult[A]) _marshalJSONPageResultLb_A_bL(x PageResult[A]) ([]byte, if fieldNext != nil { partial["Next"] = fieldNext } + var fieldPrev []byte + fieldPrev, err = r._marshalJSONPtrFindingRecordsLb_A_bL(x.Prev) + if err != nil { + return nil, fmt.Errorf("schemaless: PageResult[A]._marshalJSONPageResultLb_A_bL: field name Prev; %w", err) + } + if fieldPrev != nil { + partial["Prev"] = fieldPrev + } result, err := json.Marshal(partial) if err != nil { return nil, fmt.Errorf("schemaless: PageResult[A]._marshalJSONPageResultLb_A_bL: struct; %w", err) @@ -224,6 +232,12 @@ func (r *PageResult[A]) _unmarshalJSONPageResultLb_A_bL(data []byte) (PageResult return result, fmt.Errorf("schemaless: PageResult[A]._unmarshalJSONPageResultLb_A_bL: field Next; %w", err) } } + if fieldPrev, ok := partial["Prev"]; ok { + result.Prev, err = r._unmarshalJSONPtrFindingRecordsLb_A_bL(fieldPrev) + if err != nil { + return result, fmt.Errorf("schemaless: PageResult[A]._unmarshalJSONPageResultLb_A_bL: field Prev; %w", err) + } + } return result, nil } func (r *PageResult[A]) _unmarshalJSONSliceA(data []byte) ([]A, error) { @@ -308,6 +322,23 @@ func PageResultShape() shape.Shape { }, }, }, + { + Name: "Prev", + Type: &shape.PointerLike{ + Type: &shape.RefName{ + Name: "FindingRecords", + PkgName: "schemaless", + PkgImportName: "github.com/widmogrod/mkunion/x/storage/schemaless", + Indexed: []shape.Shape{ + &shape.RefName{ + Name: "A", + PkgName: "", + PkgImportName: "", + }, + }, + }, + }, + }, }, Tags: map[string]shape.Tag{ "serde": { diff --git a/x/storage/schemaless/typedful/typed_stream.go b/x/storage/schemaless/typedful/typed_stream.go index b6b93e74..80a9b8c2 100644 --- a/x/storage/schemaless/typedful/typed_stream.go +++ b/x/storage/schemaless/typedful/typed_stream.go @@ -8,7 +8,7 @@ import ( ) func NewTypedAppendLog[T any](log schemaless.AppendLoger[schema.Schema]) *TypedAppendLog[T] { - location, err := NewTypedLocation[schemaless.Record[T]]() + location, err := schema.NewTypedLocation[schemaless.Record[T]]() if err != nil { panic(fmt.Errorf("typedful.NewTypedRepoWithAggregator: %w", err)) } @@ -21,7 +21,7 @@ func NewTypedAppendLog[T any](log schemaless.AppendLoger[schema.Schema]) *TypedA type TypedAppendLog[T any] struct { log schemaless.AppendLoger[schema.Schema] - loc *TypedLocation + loc *schema.TypedLocation } func (t *TypedAppendLog[T]) Close() { diff --git a/x/storage/schemaless/typedful/with_aggregator.go b/x/storage/schemaless/typedful/with_aggregator.go index d7b7e343..8efb7bd5 100644 --- a/x/storage/schemaless/typedful/with_aggregator.go +++ b/x/storage/schemaless/typedful/with_aggregator.go @@ -12,7 +12,7 @@ func NewTypedRepoWithAggregator[T, C any]( store Repository[schema.Schema], aggregator func() Aggregator[T, C], ) *TypedRepoWithAggregator[T, C] { - location, err := NewTypedLocation[Record[T]]() + location, err := schema.NewTypedLocation[Record[T]]() if err != nil { panic(fmt.Errorf("typedful.NewTypedRepoWithAggregator: %w", err)) } @@ -27,7 +27,7 @@ func NewTypedRepoWithAggregator[T, C any]( var _ Repository[any] = &TypedRepoWithAggregator[any, any]{} type TypedRepoWithAggregator[T any, C any] struct { - loc *TypedLocation + loc *schema.TypedLocation store Repository[schema.Schema] aggregator func() Aggregator[T, C] } @@ -124,6 +124,7 @@ func (repo *TypedRepoWithAggregator[T, C]) FindingRecords(query FindingRecords[R Sort: query.Sort, Limit: query.Limit, After: query.After, + Before: query.Before, }) if err != nil { return PageResult[Record[T]]{}, fmt.Errorf("store.TypedRepoWithAggregator.FindingRecords store error %w", err) @@ -136,10 +137,23 @@ func (repo *TypedRepoWithAggregator[T, C]) FindingRecords(query FindingRecords[R if found.HasNext() { result.Next = &FindingRecords[Record[T]]{ - Where: query.Where, - Sort: query.Sort, - Limit: query.Limit, - After: found.Next.After, + RecordType: query.RecordType, + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + After: found.Next.After, + Before: nil, + } + } + + if found.HasPrev() { + result.Prev = &FindingRecords[Record[T]]{ + RecordType: query.RecordType, + Where: query.Where, + Sort: query.Sort, + Limit: query.Limit, + After: nil, + Before: found.Prev.Before, } } diff --git a/x/workflow/workflow_machine_serde_gen.go b/x/workflow/workflow_machine_serde_gen.go index 0bbcb214..0d5cab5f 100644 --- a/x/workflow/workflow_machine_serde_gen.go +++ b/x/workflow/workflow_machine_serde_gen.go @@ -10,78 +10,78 @@ import ( ) func init() { - shape.Register(ResumeOptionsShape()) - shape.Register(BaseStateShape()) shape.Register(ApplyAwaitOptionsShape()) + shape.Register(BaseStateShape()) + shape.Register(ResumeOptionsShape()) } var ( - _ json.Unmarshaler = (*ResumeOptions)(nil) - _ json.Marshaler = (*ResumeOptions)(nil) + _ json.Unmarshaler = (*ApplyAwaitOptions)(nil) + _ json.Marshaler = (*ApplyAwaitOptions)(nil) ) -func (r *ResumeOptions) MarshalJSON() ([]byte, error) { +func (r *ApplyAwaitOptions) MarshalJSON() ([]byte, error) { if r == nil { return nil, nil } - return r._marshalJSONResumeOptions(*r) + return r._marshalJSONApplyAwaitOptions(*r) } -func (r *ResumeOptions) _marshalJSONResumeOptions(x ResumeOptions) ([]byte, error) { +func (r *ApplyAwaitOptions) _marshalJSONApplyAwaitOptions(x ApplyAwaitOptions) ([]byte, error) { partial := make(map[string]json.RawMessage) var err error var fieldTimeout []byte fieldTimeout, err = r._marshalJSONint64(x.Timeout) if err != nil { - return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONResumeOptions: field name Timeout; %w", err) + return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONApplyAwaitOptions: field name Timeout; %w", err) } partial["Timeout"] = fieldTimeout result, err := json.Marshal(partial) if err != nil { - return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONResumeOptions: struct; %w", err) + return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONApplyAwaitOptions: struct; %w", err) } return result, nil } -func (r *ResumeOptions) _marshalJSONint64(x int64) ([]byte, error) { +func (r *ApplyAwaitOptions) _marshalJSONint64(x int64) ([]byte, error) { result, err := json.Marshal(x) if err != nil { - return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONint64:; %w", err) + return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONint64:; %w", err) } return result, nil } -func (r *ResumeOptions) UnmarshalJSON(data []byte) error { - result, err := r._unmarshalJSONResumeOptions(data) +func (r *ApplyAwaitOptions) UnmarshalJSON(data []byte) error { + result, err := r._unmarshalJSONApplyAwaitOptions(data) if err != nil { - return fmt.Errorf("workflow: ResumeOptions.UnmarshalJSON: %w", err) + return fmt.Errorf("workflow: ApplyAwaitOptions.UnmarshalJSON: %w", err) } *r = result return nil } -func (r *ResumeOptions) _unmarshalJSONResumeOptions(data []byte) (ResumeOptions, error) { - result := ResumeOptions{} +func (r *ApplyAwaitOptions) _unmarshalJSONApplyAwaitOptions(data []byte) (ApplyAwaitOptions, error) { + result := ApplyAwaitOptions{} var partial map[string]json.RawMessage err := json.Unmarshal(data, &partial) if err != nil { - return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONResumeOptions: native struct unwrap; %w", err) + return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONApplyAwaitOptions: native struct unwrap; %w", err) } if fieldTimeout, ok := partial["Timeout"]; ok { result.Timeout, err = r._unmarshalJSONint64(fieldTimeout) if err != nil { - return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONResumeOptions: field Timeout; %w", err) + return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONApplyAwaitOptions: field Timeout; %w", err) } } return result, nil } -func (r *ResumeOptions) _unmarshalJSONint64(data []byte) (int64, error) { +func (r *ApplyAwaitOptions) _unmarshalJSONint64(data []byte) (int64, error) { var result int64 err := json.Unmarshal(data, &result) if err != nil { - return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONint64: native primitive unwrap; %w", err) + return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONint64: native primitive unwrap; %w", err) } return result, nil } -func ResumeOptionsShape() shape.Shape { +func ApplyAwaitOptionsShape() shape.Shape { return &shape.StructLike{ - Name: "ResumeOptions", + Name: "ApplyAwaitOptions", PkgName: "workflow", PkgImportName: "github.com/widmogrod/mkunion/x/workflow", Fields: []*shape.FieldLike{ @@ -398,72 +398,72 @@ func BaseStateShape() shape.Shape { } var ( - _ json.Unmarshaler = (*ApplyAwaitOptions)(nil) - _ json.Marshaler = (*ApplyAwaitOptions)(nil) + _ json.Unmarshaler = (*ResumeOptions)(nil) + _ json.Marshaler = (*ResumeOptions)(nil) ) -func (r *ApplyAwaitOptions) MarshalJSON() ([]byte, error) { +func (r *ResumeOptions) MarshalJSON() ([]byte, error) { if r == nil { return nil, nil } - return r._marshalJSONApplyAwaitOptions(*r) + return r._marshalJSONResumeOptions(*r) } -func (r *ApplyAwaitOptions) _marshalJSONApplyAwaitOptions(x ApplyAwaitOptions) ([]byte, error) { +func (r *ResumeOptions) _marshalJSONResumeOptions(x ResumeOptions) ([]byte, error) { partial := make(map[string]json.RawMessage) var err error var fieldTimeout []byte fieldTimeout, err = r._marshalJSONint64(x.Timeout) if err != nil { - return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONApplyAwaitOptions: field name Timeout; %w", err) + return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONResumeOptions: field name Timeout; %w", err) } partial["Timeout"] = fieldTimeout result, err := json.Marshal(partial) if err != nil { - return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONApplyAwaitOptions: struct; %w", err) + return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONResumeOptions: struct; %w", err) } return result, nil } -func (r *ApplyAwaitOptions) _marshalJSONint64(x int64) ([]byte, error) { +func (r *ResumeOptions) _marshalJSONint64(x int64) ([]byte, error) { result, err := json.Marshal(x) if err != nil { - return nil, fmt.Errorf("workflow: ApplyAwaitOptions._marshalJSONint64:; %w", err) + return nil, fmt.Errorf("workflow: ResumeOptions._marshalJSONint64:; %w", err) } return result, nil } -func (r *ApplyAwaitOptions) UnmarshalJSON(data []byte) error { - result, err := r._unmarshalJSONApplyAwaitOptions(data) +func (r *ResumeOptions) UnmarshalJSON(data []byte) error { + result, err := r._unmarshalJSONResumeOptions(data) if err != nil { - return fmt.Errorf("workflow: ApplyAwaitOptions.UnmarshalJSON: %w", err) + return fmt.Errorf("workflow: ResumeOptions.UnmarshalJSON: %w", err) } *r = result return nil } -func (r *ApplyAwaitOptions) _unmarshalJSONApplyAwaitOptions(data []byte) (ApplyAwaitOptions, error) { - result := ApplyAwaitOptions{} +func (r *ResumeOptions) _unmarshalJSONResumeOptions(data []byte) (ResumeOptions, error) { + result := ResumeOptions{} var partial map[string]json.RawMessage err := json.Unmarshal(data, &partial) if err != nil { - return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONApplyAwaitOptions: native struct unwrap; %w", err) + return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONResumeOptions: native struct unwrap; %w", err) } if fieldTimeout, ok := partial["Timeout"]; ok { result.Timeout, err = r._unmarshalJSONint64(fieldTimeout) if err != nil { - return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONApplyAwaitOptions: field Timeout; %w", err) + return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONResumeOptions: field Timeout; %w", err) } } return result, nil } -func (r *ApplyAwaitOptions) _unmarshalJSONint64(data []byte) (int64, error) { +func (r *ResumeOptions) _unmarshalJSONint64(data []byte) (int64, error) { var result int64 err := json.Unmarshal(data, &result) if err != nil { - return result, fmt.Errorf("workflow: ApplyAwaitOptions._unmarshalJSONint64: native primitive unwrap; %w", err) + return result, fmt.Errorf("workflow: ResumeOptions._unmarshalJSONint64: native primitive unwrap; %w", err) } return result, nil } -func ApplyAwaitOptionsShape() shape.Shape { +func ResumeOptionsShape() shape.Shape { return &shape.StructLike{ - Name: "ApplyAwaitOptions", + Name: "ResumeOptions", PkgName: "workflow", PkgImportName: "github.com/widmogrod/mkunion/x/workflow", Fields: []*shape.FieldLike{ diff --git a/x/workflow/workflow_other_serde_gen.go b/x/workflow/workflow_other_serde_gen.go index e2a2878b..92892665 100644 --- a/x/workflow/workflow_other_serde_gen.go +++ b/x/workflow/workflow_other_serde_gen.go @@ -10,8 +10,94 @@ import ( ) func init() { - shape.Register(FunctionInputShape()) shape.Register(FunctionOutputShape()) + shape.Register(FunctionInputShape()) +} + +var ( + _ json.Unmarshaler = (*FunctionOutput)(nil) + _ json.Marshaler = (*FunctionOutput)(nil) +) + +func (r *FunctionOutput) MarshalJSON() ([]byte, error) { + if r == nil { + return nil, nil + } + return r._marshalJSONFunctionOutput(*r) +} +func (r *FunctionOutput) _marshalJSONFunctionOutput(x FunctionOutput) ([]byte, error) { + partial := make(map[string]json.RawMessage) + var err error + var fieldResult []byte + fieldResult, err = r._marshalJSONschema_Schema(x.Result) + if err != nil { + return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONFunctionOutput: field name Result; %w", err) + } + partial["Result"] = fieldResult + result, err := json.Marshal(partial) + if err != nil { + return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONFunctionOutput: struct; %w", err) + } + return result, nil +} +func (r *FunctionOutput) _marshalJSONschema_Schema(x schema.Schema) ([]byte, error) { + result, err := shared.JSONMarshal[schema.Schema](x) + if err != nil { + return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONschema_Schema:; %w", err) + } + return result, nil +} +func (r *FunctionOutput) UnmarshalJSON(data []byte) error { + result, err := r._unmarshalJSONFunctionOutput(data) + if err != nil { + return fmt.Errorf("workflow: FunctionOutput.UnmarshalJSON: %w", err) + } + *r = result + return nil +} +func (r *FunctionOutput) _unmarshalJSONFunctionOutput(data []byte) (FunctionOutput, error) { + result := FunctionOutput{} + var partial map[string]json.RawMessage + err := json.Unmarshal(data, &partial) + if err != nil { + return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONFunctionOutput: native struct unwrap; %w", err) + } + if fieldResult, ok := partial["Result"]; ok { + result.Result, err = r._unmarshalJSONschema_Schema(fieldResult) + if err != nil { + return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONFunctionOutput: field Result; %w", err) + } + } + return result, nil +} +func (r *FunctionOutput) _unmarshalJSONschema_Schema(data []byte) (schema.Schema, error) { + result, err := shared.JSONUnmarshal[schema.Schema](data) + if err != nil { + return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONschema_Schema: native ref unwrap; %w", err) + } + return result, nil +} +func FunctionOutputShape() shape.Shape { + return &shape.StructLike{ + Name: "FunctionOutput", + PkgName: "workflow", + PkgImportName: "github.com/widmogrod/mkunion/x/workflow", + Fields: []*shape.FieldLike{ + { + Name: "Result", + Type: &shape.RefName{ + Name: "Schema", + PkgName: "schema", + PkgImportName: "github.com/widmogrod/mkunion/x/schema", + }, + }, + }, + Tags: map[string]shape.Tag{ + "serde": { + Value: "json", + }, + }, + } } var ( @@ -179,89 +265,3 @@ func FunctionInputShape() shape.Shape { }, } } - -var ( - _ json.Unmarshaler = (*FunctionOutput)(nil) - _ json.Marshaler = (*FunctionOutput)(nil) -) - -func (r *FunctionOutput) MarshalJSON() ([]byte, error) { - if r == nil { - return nil, nil - } - return r._marshalJSONFunctionOutput(*r) -} -func (r *FunctionOutput) _marshalJSONFunctionOutput(x FunctionOutput) ([]byte, error) { - partial := make(map[string]json.RawMessage) - var err error - var fieldResult []byte - fieldResult, err = r._marshalJSONschema_Schema(x.Result) - if err != nil { - return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONFunctionOutput: field name Result; %w", err) - } - partial["Result"] = fieldResult - result, err := json.Marshal(partial) - if err != nil { - return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONFunctionOutput: struct; %w", err) - } - return result, nil -} -func (r *FunctionOutput) _marshalJSONschema_Schema(x schema.Schema) ([]byte, error) { - result, err := shared.JSONMarshal[schema.Schema](x) - if err != nil { - return nil, fmt.Errorf("workflow: FunctionOutput._marshalJSONschema_Schema:; %w", err) - } - return result, nil -} -func (r *FunctionOutput) UnmarshalJSON(data []byte) error { - result, err := r._unmarshalJSONFunctionOutput(data) - if err != nil { - return fmt.Errorf("workflow: FunctionOutput.UnmarshalJSON: %w", err) - } - *r = result - return nil -} -func (r *FunctionOutput) _unmarshalJSONFunctionOutput(data []byte) (FunctionOutput, error) { - result := FunctionOutput{} - var partial map[string]json.RawMessage - err := json.Unmarshal(data, &partial) - if err != nil { - return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONFunctionOutput: native struct unwrap; %w", err) - } - if fieldResult, ok := partial["Result"]; ok { - result.Result, err = r._unmarshalJSONschema_Schema(fieldResult) - if err != nil { - return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONFunctionOutput: field Result; %w", err) - } - } - return result, nil -} -func (r *FunctionOutput) _unmarshalJSONschema_Schema(data []byte) (schema.Schema, error) { - result, err := shared.JSONUnmarshal[schema.Schema](data) - if err != nil { - return result, fmt.Errorf("workflow: FunctionOutput._unmarshalJSONschema_Schema: native ref unwrap; %w", err) - } - return result, nil -} -func FunctionOutputShape() shape.Shape { - return &shape.StructLike{ - Name: "FunctionOutput", - PkgName: "workflow", - PkgImportName: "github.com/widmogrod/mkunion/x/workflow", - Fields: []*shape.FieldLike{ - { - Name: "Result", - Type: &shape.RefName{ - Name: "Schema", - PkgName: "schema", - PkgImportName: "github.com/widmogrod/mkunion/x/schema", - }, - }, - }, - Tags: map[string]shape.Tag{ - "serde": { - Value: "json", - }, - }, - } -}