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 dependencies: Vec<String>, // Names of fixtures this fixture depends on (via parameters)
60 pub scope: FixtureScope, // The fixture's scope (function, class, module, package, session)
61 pub yield_line: Option<usize>, // Line number of the yield statement (for generator fixtures)
62 pub autouse: bool, // Whether this fixture has autouse=True
63}
64
65/// A fixture usage (reference) in a Python file.
66#[derive(Debug, Clone)]
67pub struct FixtureUsage {
68 pub name: String,
69 pub file_path: PathBuf,
70 pub line: usize,
71 pub start_char: usize, // Character position where this usage starts (on the line)
72 pub end_char: usize, // Character position where this usage ends (on the line)
73}
74
75/// An undeclared fixture used in a function body without being declared as a parameter.
76#[derive(Debug, Clone)]
77#[allow(dead_code)] // Fields used for debugging and future features
78pub struct UndeclaredFixture {
79 pub name: String,
80 pub file_path: PathBuf,
81 pub line: usize,
82 pub start_char: usize,
83 pub end_char: usize,
84 pub function_name: String, // Name of the test/fixture function where this is used
85 pub function_line: usize, // Line where the function is defined
86}
87
88/// A circular dependency between fixtures.
89#[derive(Debug, Clone)]
90pub struct FixtureCycle {
91 /// The chain of fixtures forming the cycle (e.g., ["A", "B", "C", "A"]).
92 pub cycle_path: Vec<String>,
93 /// The fixture where the cycle was detected (first fixture in the cycle).
94 pub fixture: FixtureDefinition,
95}
96
97/// A scope mismatch where a broader-scoped fixture depends on a narrower-scoped fixture.
98#[derive(Debug, Clone)]
99pub struct ScopeMismatch {
100 /// The fixture with broader scope that has the invalid dependency.
101 pub fixture: FixtureDefinition,
102 /// The dependency fixture with narrower scope.
103 pub dependency: FixtureDefinition,
104}
105
106/// Context for code completion.
107#[derive(Debug, Clone, PartialEq)]
108pub enum CompletionContext {
109 /// Inside a function signature (parameter list) - suggest fixtures as parameters.
110 FunctionSignature {
111 function_name: String,
112 function_line: usize,
113 is_fixture: bool,
114 declared_params: Vec<String>,
115 },
116 /// Inside a function body - suggest fixtures with auto-add to parameters.
117 FunctionBody {
118 function_name: String,
119 function_line: usize,
120 is_fixture: bool,
121 declared_params: Vec<String>,
122 },
123 /// Inside @pytest.mark.usefixtures("...") decorator - suggest fixture names as strings.
124 UsefixuturesDecorator,
125 /// Inside @pytest.mark.parametrize(..., indirect=...) - suggest fixture names as strings.
126 ParametrizeIndirect,
127}
128
129/// Information about where to insert a new parameter in a function signature.
130#[derive(Debug, Clone, PartialEq)]
131pub struct ParamInsertionInfo {
132 /// Line number (1-indexed) where the function signature is.
133 pub line: usize,
134 /// Character position where the new parameter should be inserted.
135 pub char_pos: usize,
136 /// Whether a comma needs to be added before the new parameter.
137 pub needs_comma: bool,
138}