@@ -7,6 +7,7 @@ public final class HaystackServer: API, Sendable {
77 let recordStore : RecordStore
88 let historyStore : HistoryStore
99 let watchStore : WatchStore
10+ let navPath : [ String ]
1011
1112 let onInvokeAction : @Sendable ( Haystack . Ref , String , [ String : any Haystack . Val ] ) async throws -> Haystack . Grid
1213 let onEval : @Sendable ( String) async throws -> Haystack . Grid
@@ -15,6 +16,7 @@ public final class HaystackServer: API, Sendable {
1516 recordStore: RecordStore ,
1617 historyStore: HistoryStore ,
1718 watchStore: WatchStore ,
19+ navPath: [ String ] = [ " site " , " equip " , " point " ] ,
1820 onInvokeAction: @escaping @Sendable ( Haystack . Ref , String , [ String : any Haystack . Val ] ) async throws -> Haystack . Grid = { _, _, _ in
1921 GridBuilder ( ) . toGrid ( )
2022 } ,
@@ -25,6 +27,7 @@ public final class HaystackServer: API, Sendable {
2527 self . recordStore = recordStore
2628 self . historyStore = historyStore
2729 self . watchStore = watchStore
30+ self . navPath = navPath
2831 self . onInvokeAction = onInvokeAction
2932 self . onEval = onEval
3033 }
@@ -106,9 +109,52 @@ public final class HaystackServer: API, Sendable {
106109 return gb. toGrid ( )
107110 }
108111
109- public func nav( navId _: Haystack . Ref ? ) async throws -> Haystack . Grid {
110- // TODO: Implement
111- return GridBuilder ( ) . toGrid ( )
112+ public func nav( navId parentId: Haystack . Ref ? ) async throws -> Haystack . Grid {
113+ let gb = Haystack . GridBuilder ( )
114+ try gb. addCol ( name: " navId " )
115+ guard navPath. count > 0 else {
116+ return gb. toGrid ( )
117+ }
118+ guard let parentId = parentId else {
119+ // If no input, just return the first level of navigation
120+ for result in try await recordStore. read ( filter: " \( navPath [ 0 ] ) " , limit: nil ) {
121+ var navResult = result
122+ navResult [ " navId " ] = result [ " id " ]
123+ try gb. addRow ( navResult)
124+ }
125+ return gb. toGrid ( )
126+ }
127+ guard let parentDict = try await recordStore. read ( ids: [ parentId] ) . first else {
128+ throw ServerError . idNotFound ( parentId)
129+ }
130+ // Find the first component of the navPath that matches a tag on the input dict
131+ var parentNavPathIndex: Int? = nil
132+ for index in 0 ..< navPath. count {
133+ let component = navPath [ index]
134+ if parentDict. has ( component) {
135+ parentNavPathIndex = index
136+ }
137+ }
138+ guard let parentNavPathIndex = parentNavPathIndex else {
139+ throw ServerError . navPathComponentNotFound ( navPath)
140+ }
141+ guard parentNavPathIndex < navPath. count - 1 else {
142+ // Parent is a navPath leaf. No further navigation is possible, so return nothing.
143+ return gb. toGrid ( )
144+ }
145+ let parentNavComponent = navPath [ parentNavPathIndex]
146+ let childNavComponent = navPath [ parentNavPathIndex + 1 ]
147+ // Read children using child component and inferring parent ref tag
148+ let children = try await recordStore. read (
149+ filter: " \( childNavComponent) and \( parentNavComponent) Ref == \( parentId. toZinc ( ) ) " ,
150+ limit: nil
151+ )
152+ for child in children {
153+ var navChild = child
154+ navChild [ " navId " ] = child [ " id " ]
155+ try gb. addRow ( navChild)
156+ }
157+ return gb. toGrid ( )
112158 }
113159
114160 public func hisRead ( id: Haystack . Ref, range: Haystack . HisReadRange) async throws -> Haystack. Grid {
@@ -199,5 +245,6 @@ public final class HaystackServer: API, Sendable {
199245
200246public enum ServerError: Error {
201247 case idNotFound( Haystack . Ref)
248+ case navPathComponentNotFound( [ String] )
202249 case watchNotFound( String)
203250}
0 commit comments