lib.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. //! codegraph-kernel — native extraction kernel (napi-rs).
  2. //!
  3. //! Replaces ONLY the parse+extract walk inside the parse workers, behind the
  4. //! existing `ExtractionResult` contract. Input `(filePath, content, language)`
  5. //! per file; output flat typed buffers — one boundary crossing per file.
  6. //! Everything downstream (resolution, synthesis, frameworks, MCP) is
  7. //! untouched and consumes the decoded result exactly as before.
  8. //!
  9. //! Calls are synchronous by design: the existing `ParseWorkerPool` workers
  10. //! already parallelize per-file, so each worker thread drives its own kernel
  11. //! call (do NOT rebuild the pool on the Rust side — see the migration plan §3).
  12. //!
  13. //! Per-language extraction lives in a dedicated walker module (tsjs/ for
  14. //! typescript/tsx/javascript/jsx) that mirrors the TS extractor for behavioral
  15. //! parity — verified by scripts/kernel-parity.mjs and the §5 gate.
  16. #![deny(clippy::all)]
  17. mod buffers;
  18. mod ccpp;
  19. mod docstring;
  20. mod ids;
  21. mod go;
  22. mod java;
  23. mod langs;
  24. mod textutil;
  25. mod python;
  26. mod tsjs;
  27. use napi::bindgen_prelude::*;
  28. use napi_derive::napi;
  29. /// The five flat tables for one file. See buffers.rs for the byte layout;
  30. /// `src/extraction/kernel/layout.ts` is the TS mirror.
  31. #[napi(object)]
  32. pub struct ExtractBuffers {
  33. pub meta: Buffer,
  34. pub nodes: Buffer,
  35. pub edges: Buffer,
  36. pub refs: Buffer,
  37. pub arena: Buffer,
  38. }
  39. /// Wire-contract description — the TS loader verifies this against
  40. /// src/types.ts before routing anything to the kernel, so an out-of-date
  41. /// `.node` degrades to the wasm path instead of mis-decoding.
  42. #[napi(object)]
  43. pub struct ContractInfo {
  44. pub abi_version: u32,
  45. pub kernel_version: String,
  46. pub node_kinds: Vec<String>,
  47. pub edge_kinds: Vec<String>,
  48. /// Languages this binary can extract (routing is still TS-side policy).
  49. pub languages: Vec<String>,
  50. }
  51. /// Grammar identity for the grammar-source-parity gate: the wasm grammar and
  52. /// the native grammar must expose identical node-kind/field tables, or
  53. /// kernel-vs-fallback routing would be non-deterministic.
  54. #[napi(object)]
  55. pub struct GrammarInfo {
  56. pub abi_version: u32,
  57. pub node_kind_count: u32,
  58. pub field_count: u32,
  59. pub node_kinds: Vec<String>,
  60. pub field_names: Vec<String>,
  61. }
  62. #[napi]
  63. pub fn contract_info() -> ContractInfo {
  64. ContractInfo {
  65. abi_version: buffers::KERNEL_ABI_VERSION as u32,
  66. kernel_version: env!("CARGO_PKG_VERSION").to_string(),
  67. node_kinds: buffers::NODE_KINDS.iter().map(|s| s.to_string()).collect(),
  68. edge_kinds: buffers::EDGE_KINDS.iter().map(|s| s.to_string()).collect(),
  69. languages: langs::LANGUAGES.iter().map(|s| s.to_string()).collect(),
  70. }
  71. }
  72. #[napi]
  73. pub fn grammar_info(language: String) -> Option<GrammarInfo> {
  74. let lang = langs::grammar_for(&language)?;
  75. let node_kind_count = lang.node_kind_count();
  76. let field_count = lang.field_count();
  77. let node_kinds = (0..node_kind_count)
  78. .map(|i| lang.node_kind_for_id(i as u16).unwrap_or("").to_string())
  79. .collect();
  80. // Field ids are 1-based in tree-sitter.
  81. let field_names = (1..=field_count)
  82. .map(|i| lang.field_name_for_id(i as u16).unwrap_or("").to_string())
  83. .collect();
  84. Some(GrammarInfo {
  85. abi_version: lang.abi_version() as u32,
  86. node_kind_count: node_kind_count as u32,
  87. field_count: field_count as u32,
  88. node_kinds,
  89. field_names,
  90. })
  91. }
  92. #[napi]
  93. pub fn extract_file(file_path: String, content: String, language: String) -> Result<ExtractBuffers> {
  94. let out = match language.as_str() {
  95. "java" => java::extract(&file_path, &content).map_err(Error::from_reason)?,
  96. "python" => python::extract(&file_path, &content).map_err(Error::from_reason)?,
  97. "go" => go::extract(&file_path, &content).map_err(Error::from_reason)?,
  98. "c" | "cpp" => ccpp::extract(&file_path, &content, &language).map_err(Error::from_reason)?,
  99. _ => tsjs::extract(&file_path, &content, &language).map_err(Error::from_reason)?,
  100. };
  101. Ok(ExtractBuffers {
  102. meta: out.meta.into(),
  103. nodes: out.nodes.into(),
  104. edges: out.edges.into(),
  105. refs: out.refs.into(),
  106. arena: out.arena.into(),
  107. })
  108. }