Skip to main content

monty_types/
type_checking.rs

1use std::str::FromStr;
2
3use serde::{Deserialize, Serialize};
4use strum::VariantNames;
5
6/// How type-check diagnostics are rendered into text.
7///
8/// Mirrors ty's `DiagnosticFormat`. Rendering happens wherever the type checker
9/// runs (inside the worker for pool sessions), because ty's structured
10/// diagnostics borrow the salsa database and cannot cross a process boundary —
11/// so the format has to be chosen before the check, not after it.
12///
13/// Serialized into session dumps by discriminant, so new variants must be
14/// appended — inserting one shifts every later variant and silently rewrites
15/// older dumps' format (see `DUMP_VERSION` in `monty`).
16#[derive(
17    Debug,
18    Clone,
19    Copy,
20    Default,
21    PartialEq,
22    Eq,
23    Serialize,
24    Deserialize,
25    strum::Display,
26    strum::EnumString,
27    strum::VariantNames,
28)]
29#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
30pub enum TypeCheckingFormat {
31    /// Human-readable diagnostics with a source snippet and carets.
32    #[default]
33    Full,
34    /// One `path:line:col: severity[rule] message` line per diagnostic.
35    Concise,
36    /// Azure Pipelines logging commands.
37    Azure,
38    /// A JSON array of diagnostic objects.
39    Json,
40    /// One JSON diagnostic object per line.
41    #[strum(to_string = "jsonlines", serialize = "json-lines")]
42    JsonLines,
43    /// Reviewdog diagnostic JSON.
44    Rdjson,
45    /// Pylint-compatible output.
46    Pylint,
47    /// GitLab Code Quality report JSON.
48    Gitlab,
49    /// GitHub Actions workflow commands.
50    Github,
51}
52
53impl TypeCheckingFormat {
54    /// Parses a format name, reporting the valid names on failure.
55    ///
56    /// Bindings take the format as a string, so the error has to be good
57    /// enough to show a user who guessed wrong.
58    pub fn from_name(name: &str) -> Result<Self, String> {
59        Self::from_str(name)
60            .map_err(|_| format!("unknown type check format '{name}', expected one of: {}", Self::names()))
61    }
62
63    /// Comma-separated list of the accepted format names.
64    #[must_use]
65    pub fn names() -> String {
66        Self::VARIANTS.join(", ")
67    }
68}
69
70/// How a type check renders whatever diagnostics it finds.
71#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
72pub struct TypeCheckingConfig {
73    /// Output format.
74    pub format: TypeCheckingFormat,
75    /// Whether to include ANSI colour escapes. Only `Full` and `Concise`
76    /// render any colour; the machine-readable formats ignore it.
77    pub color: bool,
78}
79
80/// Per-session type-check state: successfully committed snippets accumulate as
81/// stubs so later snippets can reference names defined by earlier ones.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct TypeCheckState {
84    /// User-provided stubs plus every snippet that has completed successfully.
85    pub committed_stubs: String,
86    /// The in-flight snippet; committed on success, discarded on error.
87    pub pending_snippet: Option<String>,
88    /// How diagnostics are rendered by whoever runs the type checker.
89    pub config: TypeCheckingConfig,
90}