Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mcp

Model Context Protocol server for Pudu language documentation and compiler tools

0.1.1Apache-2.01

InstallClose

Context.pudu

Pudu82 lines3.0 KB

GitHub ↗
1/** @App.Context.Aggregate — the resources every request handler shares */2module PuduLangMcp.App.Context34import Std.Concurrent as Concurrent5import Std.Env as Env6import Std.Result as Result7import Std.Sync as Sync8import PuduLangMcp.Constants.Server as Server9import PuduLangMcp.Domain.Docs.Chapter as Chapter10import PuduLangMcp.Domain.Reference.Entry as Entry11import PuduLangMcp.Errors.ToolError as ToolError12import PuduLangMcp.Services.Reference as Reference13import PuduLangMcp.Services.Toolchain as Toolchain14import PuduLangMcp.Services.Workspace as Workspace1516/** @App.Context.IndexState — progress of the background reference index */17export type IndexState = Building | Ready(Array[Entry.Entry]) | Broken(Str)1819/** @App.Context.Context — toolchain, workspace, corpus, and index for handlers */20export type Context = {21  toolchain: Toolchain.Toolchain,22  workspace: Workspace.Workspace,23  chapters: Array[Chapter.Chapter],24  sections: Array[Chapter.Section],25  moduleFiles: Array[Reference.ModuleFile],26  docsRevision: Str,27  index: Sync.Cell[IndexState]28}2930/// How often a request waiting for the index looks at it again.31const INDEX_POLL_MS: Int = 503233/// The context for a toolchain, workspace, and corpus, with the index building in the background.34export fn create(toolchain: Toolchain.Toolchain, workspace: Workspace.Workspace, chapters: Array[Chapter.Chapter], docsRevision: Str) -> Context {35  let files = Reference.moduleFiles(toolchain.library, workspace.root)36  let index = Sync.cell(Building)37  let context = Context {38    toolchain: toolchain,39    workspace: workspace,40    chapters: chapters,41    sections: Chapter.allSections(&chapters),42    moduleFiles: files,43    docsRevision: docsRevision,44    index: index45  }46  let builder = context.toolchain47  let target = context.index48  let started = Concurrent.start(fn() -> () {49      let state = match Reference.build(&builder, &files) {50        case Ok(found) => Ready(found)51        case Err(problem) => Broken(ToolError.explain(&problem))52      }53      let _stored = Sync.set(&target, state)54    })55  if Result.isErr(&started) {56    let _stored = Sync.set(&context.index, Broken("The reference index could not be started."))57  }58  context59}6061/// The reference index, waiting a bounded time while it is still being built.62export fn entries(context: &Context) -> Result[Array[Entry.Entry], ToolError.ToolFailure] {63  let deadline = Env.elapsedMilliseconds() + Server.INDEX_WAIT_MS64  loop {65    match Result.unwrapOr(Sync.get(&context.index), Broken("The reference index could not be read.")) {66      case Ready(found) => { return Ok(found) }67      case Broken(reason) => { return Err(ToolError.Unavailable(reason)) }68      case Building => {69        if indexDeadlineReached(Env.elapsedMilliseconds(), deadline) {70          return Err(ToolError.Unavailable("The reference index is still being built from the installed toolchain; try again shortly."))71        }72        let _slept = Concurrent.sleep(INDEX_POLL_MS)73      }74    }75  }76}7778/// Whether the reference-index wait has reached its deadline.79export fn indexDeadlineReached(now: Int, deadline: Int) -> Bool {80  now >= deadline81}82