Skip to main content

pytest_language_server/fixtures/
types.rs

1//! Data structures for fixture definitions, usages, and related types.
2
3use std::path::PathBuf;
4
5/// Pytest fixture scope, ordered from narrowest to broadest.
6/// A fixture with a broader scope cannot depend on a fixture with a narrower scope.
7#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub enum FixtureScope {
9    /// Function scope (default) - created once per test function
10    #[default]
11    Function = 0,
12    /// Class scope - created once per test class
13    Class = 1,
14    /// Module scope - created once per test module
15    Module = 2,
16    /// Package scope - created once per test package
17    Package = 3,
18    /// Session scope - created once per test session
19    Session = 4,
20}
21
22impl FixtureScope {
23    /// Parse scope from a string (as used in @pytest.fixture(scope="..."))
24    pub fn parse(s: &str) -> Option<Self> {
25        match s.to_lowercase().as_str() {
26            "function" => Some(Self::Function),
27            "class" => Some(Self::Class),
28            "module" => Some(Self::Module),
29            "package" => Some(Self::Package),
30            "session" => Some(Self::Session),
31            _ => None,
32        }
33    }
34
35    /// Get display name for the scope
36    pub fn as_str(&self) -> &'static str {
37        match self {
38            Self::Function => "function",
39            Self::Class => "class",
40            Self::Module => "module",
41            Self::Package => "package",
42            Self::Session => "session",
43        }
44    }
45}
46
47/// A fixture definition extracted from a Python file.
48#[derive(Debug, Clone, PartialEq)]
49pub struct FixtureDefinition {
50    pub name: String,
51    pub file_path: PathBuf,
52    pub line: usize,
53    pub end_line: usize, // Line number where the function ends (for document symbol ranges)
54    pub start_char: usize, // Character position where the fixture name starts (on the line)
55    pub end_char: usize, // Character position where the fixture name ends (on the line)
56    pub docstring: Option<String>,
57    pub return_type: Option<String>, // The return type annotation (for generators, the yielded type)
58    pub is_third_party: bool, // Whether this fixture is from a third-party package (site-packages)
59    pub is_plugin: bool, // Whether this fixture was discovered via a pytest11 entry point plugin
60    pub dependencies: Vec<String>, // Names of fixtures this fixture depends on (via parameters)
61    pub scope: FixtureScope, // The fixture's scope (function, class, module, package, session)
62    pub yield_line: Option<usize>, // Line number of the yield statement (for generator fixtures)
63    pub autouse: bool,   // Whether this fixture has autouse=True
64}
65
66/// A fixture usage (reference) in a Python file.
67#[derive(Debug, Clone)]
68pub struct FixtureUsage {
69    pub name: String,
70    pub file_path: PathBuf,
71    pub line: usize,
72    pub start_char: usize, // Character position where this usage starts (on the line)
73    pub end_char: usize,   // Character position where this usage ends (on the line)
74}
75
76/// An undeclared fixture used in a function body without being declared as a parameter.
77#[derive(Debug, Clone)]
78#[allow(dead_code)] // Fields used for debugging and future features
79pub struct UndeclaredFixture {
80    pub name: String,
81    pub file_path: PathBuf,
82    pub line: usize,
83    pub start_char: usize,
84    pub end_char: usize,
85    pub function_name: String, // Name of the test/fixture function where this is used
86    pub function_line: usize,  // Line where the function is defined
87}
88
89/// A circular dependency between fixtures.
90#[derive(Debug, Clone)]
91pub struct FixtureCycle {
92    /// The chain of fixtures forming the cycle (e.g., ["A", "B", "C", "A"]).
93    pub cycle_path: Vec<String>,
94    /// The fixture where the cycle was detected (first fixture in the cycle).
95    pub fixture: FixtureDefinition,
96}
97
98/// A scope mismatch where a broader-scoped fixture depends on a narrower-scoped fixture.
99#[derive(Debug, Clone)]
100pub struct ScopeMismatch {
101    /// The fixture with broader scope that has the invalid dependency.
102    pub fixture: FixtureDefinition,
103    /// The dependency fixture with narrower scope.
104    pub dependency: FixtureDefinition,
105}
106
107/// Context for code completion.
108#[derive(Debug, Clone, PartialEq)]
109pub enum CompletionContext {
110    /// Inside a function signature (parameter list) - suggest fixtures as parameters.
111    FunctionSignature {
112        function_name: String,
113        function_line: usize,
114        is_fixture: bool,
115        declared_params: Vec<String>,
116        /// The fixture's scope if inside a fixture function, None for test functions.
117        fixture_scope: Option<FixtureScope>,
118    },
119    /// Inside a function body - suggest fixtures with auto-add to parameters.
120    FunctionBody {
121        function_name: String,
122        function_line: usize,
123        is_fixture: bool,
124        declared_params: Vec<String>,
125        /// The fixture's scope if inside a fixture function, None for test functions.
126        fixture_scope: Option<FixtureScope>,
127    },
128    /// Inside @pytest.mark.usefixtures("...") decorator - suggest fixture names as strings.
129    UsefixturesDecorator,
130    /// Inside @pytest.mark.parametrize(..., indirect=...) - suggest fixture names as strings.
131    ParametrizeIndirect,
132}
133
134/// Information about where to insert a new parameter in a function signature.
135#[derive(Debug, Clone, PartialEq)]
136pub struct ParamInsertionInfo {
137    /// Line number (1-indexed) where the function signature is.
138    pub line: usize,
139    /// Character position where the new parameter should be inserted.
140    pub char_pos: usize,
141    /// Whether a comma needs to be added before the new parameter.
142    pub needs_comma: bool,
143}