stackharbinger 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,11 @@ module Harbinger
31
31
 
32
32
  content = File.read(file_path)
33
33
  match = content.match(/^\s*rails\s+\(([^)]+)\)/)
34
- match ? match[1] : nil
34
+ return nil unless match
35
+
36
+ version_string = match[1]
37
+ # Strip version constraint operators (>=, ~>, =, etc.) and extract actual version
38
+ version_string.sub(/^[><=~!\s]+/, "").strip
35
39
  rescue StandardError
36
40
  nil
37
41
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "docker_compose_detector"
4
+
5
+ module Harbinger
6
+ module Analyzers
7
+ class RustDetector
8
+ def initialize(project_path)
9
+ @project_path = project_path
10
+ end
11
+
12
+ def detect
13
+ detect_from_rust_toolchain ||
14
+ detect_from_cargo_toml ||
15
+ detect_from_docker_compose ||
16
+ (rust_detected? ? detect_from_shell : nil)
17
+ end
18
+
19
+ def rust_detected?
20
+ File.exist?(File.join(project_path, "Cargo.toml")) ||
21
+ File.exist?(File.join(project_path, "Cargo.lock")) ||
22
+ File.exist?(File.join(project_path, "rust-toolchain")) ||
23
+ File.exist?(File.join(project_path, "rust-toolchain.toml")) ||
24
+ (File.directory?(File.join(project_path, "src")) && has_rust_files?)
25
+ rescue StandardError
26
+ false
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :project_path
32
+
33
+ def detect_from_rust_toolchain
34
+ # Try rust-toolchain.toml first (TOML format)
35
+ toml_path = File.join(project_path, "rust-toolchain.toml")
36
+ if File.exist?(toml_path)
37
+ content = File.read(toml_path)
38
+ # Parse: channel = "1.75.0" or channel = "stable"
39
+ match = content.match(/channel\s*=\s*["']([^"']+)["']/)
40
+ if match
41
+ channel = match[1]
42
+ # Skip "stable", "beta", "nightly" - we need specific versions
43
+ return extract_version(channel) unless channel =~ /^(stable|beta|nightly)$/
44
+ end
45
+ end
46
+
47
+ # Try plain rust-toolchain file
48
+ plain_path = File.join(project_path, "rust-toolchain")
49
+ if File.exist?(plain_path)
50
+ content = File.read(plain_path).strip
51
+ return extract_version(content) unless content.empty? || content =~ /^(stable|beta|nightly)$/
52
+ end
53
+
54
+ nil
55
+ rescue StandardError
56
+ nil
57
+ end
58
+
59
+ def detect_from_cargo_toml
60
+ file_path = File.join(project_path, "Cargo.toml")
61
+ return nil unless File.exist?(file_path)
62
+
63
+ content = File.read(file_path)
64
+
65
+ # Parse: rust-version = "1.70" or rust-version = "1.70.0"
66
+ # This is the MSRV (Minimum Supported Rust Version)
67
+ match = content.match(/rust-version\s*=\s*["']([^"']+)["']/)
68
+ return extract_version(match[1]) if match
69
+
70
+ nil
71
+ rescue StandardError
72
+ nil
73
+ end
74
+
75
+ def detect_from_docker_compose
76
+ docker = DockerComposeDetector.new(project_path)
77
+ docker.image_version("rust")
78
+ end
79
+
80
+ def detect_from_shell
81
+ output = `rustc --version 2>&1`.strip
82
+ return nil unless $CHILD_STATUS.success?
83
+
84
+ # Parse: "rustc 1.75.0 (82e1608df 2023-12-21)"
85
+ match = output.match(/rustc\s+(\d+\.\d+(?:\.\d+)?)/)
86
+ match[1] if match
87
+ rescue StandardError
88
+ nil
89
+ end
90
+
91
+ def extract_version(version_string)
92
+ # Remove "rust-" prefix if present
93
+ version = version_string.sub(/^rust-/, "")
94
+
95
+ # Strip any whitespace
96
+ version = version.strip
97
+
98
+ # Handle version with date: "1.75.0-2023-12-21" -> "1.75.0"
99
+ version = version.split("-").first if version.include?("-")
100
+
101
+ # Return major.minor or major.minor.patch format
102
+ # endoflife.date uses "1.75" format, but "1.75.0" also works
103
+ version
104
+ end
105
+
106
+ def has_rust_files?
107
+ src_dir = File.join(project_path, "src")
108
+ return false unless File.directory?(src_dir)
109
+
110
+ Dir.glob(File.join(src_dir, "**/*.rs")).any?
111
+ rescue StandardError
112
+ false
113
+ end
114
+ end
115
+ end
116
+ end