Coverage for src/jsoncrack_for_sphinx/patterns/pattern_strategies.py: 100%

61 statements  

« prev     ^ index     » next       coverage.py v7.10.0, created at 2025-07-24 22:26 +0000

1""" 

2Pattern generation strategies. 

3""" 

4 

5from typing import List, Tuple 

6 

7from ..search.search_policy import SearchPolicy 

8from ..utils.types import PathSeparator 

9 

10 

11def join_with_separator(parts_list: List[str], separator: PathSeparator) -> str: 

12 """Join parts with the specified separator.""" 

13 if separator == PathSeparator.DOT: 

14 return ".".join(parts_list) 

15 elif separator == PathSeparator.SLASH: 

16 return "/".join(parts_list) 

17 elif separator == PathSeparator.NONE: 

18 return "".join(parts_list) 

19 else: 

20 return ".".join(parts_list) # fallback 

21 

22 

23def add_class_method_patterns( 

24 parts: List[str], search_policy: SearchPolicy 

25) -> List[Tuple[str, str]]: 

26 """Add class.method patterns.""" 

27 class_method_parts = parts[-2:] # Last 2 parts 

28 class_method = join_with_separator( 

29 class_method_parts, search_policy.path_to_class_separator 

30 ) 

31 return [ 

32 (f"{class_method}.schema.json", "schema"), 

33 (f"{class_method}.json", "json"), 

34 ] 

35 

36 

37def add_path_component_patterns( 

38 parts: List[str], search_policy: SearchPolicy 

39) -> List[Tuple[str, str]]: 

40 """Add intermediate path component patterns.""" 

41 patterns = [] 

42 

43 # Generate intermediate patterns like "catalog.ProductService.similar" 

44 for i in range(len(parts) - 3, 0, -1): 

45 partial_parts = parts[i:] 

46 if len(partial_parts) >= 3: 

47 partial_path = join_with_separator( 

48 partial_parts, search_policy.path_to_file_separator 

49 ) 

50 patterns.extend( 

51 [ 

52 (f"{partial_path}.schema.json", "schema"), 

53 (f"{partial_path}.json", "json"), 

54 ] 

55 ) 

56 

57 # Include intermediate path components without package name 

58 if not search_policy.include_package_name and len(parts) >= 3: 

59 without_package = parts[1:] 

60 if search_policy.path_to_file_separator == PathSeparator.SLASH: 

61 patterns.extend( 

62 add_slash_separated_patterns(without_package, search_policy) 

63 ) 

64 else: 

65 full_path = join_with_separator( 

66 without_package, search_policy.path_to_file_separator 

67 ) 

68 patterns.extend( 

69 [ 

70 (f"{full_path}.schema.json", "schema"), 

71 (f"{full_path}.json", "json"), 

72 ] 

73 ) 

74 

75 return patterns 

76 

77 

78def add_package_name_patterns( 

79 parts: List[str], search_policy: SearchPolicy 

80) -> List[Tuple[str, str]]: 

81 """Add patterns that include package name.""" 

82 patterns = [] 

83 

84 if search_policy.path_to_file_separator == PathSeparator.SLASH: 

85 if len(parts) >= 2: 

86 dir_parts = parts[:-2] 

87 class_method_parts = parts[-2:] 

88 class_method = join_with_separator( 

89 class_method_parts, search_policy.path_to_class_separator 

90 ) 

91 if dir_parts: 

92 dir_path = "/".join(dir_parts) 

93 patterns.extend( 

94 [ 

95 (f"{dir_path}/{class_method}.schema.json", "schema"), 

96 (f"{dir_path}/{class_method}.json", "json"), 

97 ] 

98 ) 

99 else: 

100 full_path = join_with_separator(parts, search_policy.path_to_file_separator) 

101 patterns.extend( 

102 [ 

103 (f"{full_path}.schema.json", "schema"), 

104 (f"{full_path}.json", "json"), 

105 ] 

106 ) 

107 

108 return patterns 

109 

110 

111def add_slash_separated_patterns( 

112 without_package: List[str], search_policy: SearchPolicy 

113) -> List[Tuple[str, str]]: 

114 """Add patterns for slash-separated directory structure.""" 

115 patterns = [] 

116 

117 if len(without_package) >= 2: 

118 dir_parts = without_package[:-2] 

119 class_method_parts = without_package[-2:] 

120 class_method = join_with_separator( 

121 class_method_parts, search_policy.path_to_class_separator 

122 ) 

123 if dir_parts: 

124 dir_path = "/".join(dir_parts) 

125 patterns.extend( 

126 [ 

127 (f"{dir_path}/{class_method}.schema.json", "schema"), 

128 (f"{dir_path}/{class_method}.json", "json"), 

129 ] 

130 ) 

131 

132 return patterns 

133 

134 

135def remove_duplicates(patterns: List[Tuple[str, str]]) -> List[Tuple[str, str]]: 

136 """Remove duplicate patterns while preserving order.""" 

137 seen = set() 

138 unique_patterns = [] 

139 for pattern, file_type in patterns: 

140 key = (pattern, file_type) 

141 if key not in seen: 

142 seen.add(key) 

143 unique_patterns.append((pattern, file_type)) 

144 return unique_patterns