workhelix_cli_common/
license.rs

1//! License display module.
2//!
3//! This module provides standardized license information display for common open source licenses.
4
5use crate::output;
6
7/// Supported license types.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum LicenseType {
10    /// MIT License
11    MIT,
12    /// Apache License 2.0
13    Apache2,
14    /// Creative Commons CC0 1.0 Universal
15    CC0,
16}
17
18impl LicenseType {
19    /// Parse a license type from a string.
20    ///
21    /// Recognizes common variations like "MIT", "Apache-2.0", "CC0-1.0", etc.
22    #[must_use]
23    pub fn parse(s: &str) -> Option<Self> {
24        match s.to_uppercase().as_str() {
25            "MIT" => Some(Self::MIT),
26            "APACHE-2.0" | "APACHE2" | "APACHE" => Some(Self::Apache2),
27            "CC0-1.0" | "CC0" => Some(Self::CC0),
28            _ => None,
29        }
30    }
31
32    /// Get the license name.
33    #[must_use]
34    pub const fn name(self) -> &'static str {
35        match self {
36            Self::MIT => "MIT",
37            Self::Apache2 => "Apache-2.0",
38            Self::CC0 => "CC0-1.0",
39        }
40    }
41}
42
43/// Display license information for a tool.
44///
45/// # Arguments
46/// * `tool_name` - Name of the tool
47/// * `license` - License type
48///
49/// # Returns
50/// Formatted license information string
51#[must_use]
52pub fn display_license(tool_name: &str, license: LicenseType) -> String {
53    let mut output = format!("{tool_name} is licensed under {}\n\n", license.name());
54
55    match license {
56        LicenseType::MIT => {
57            output.push_str("MIT License - A permissive license that allows:\n");
58            output.push_str("• Commercial use\n");
59            output.push_str("• Modification\n");
60            output.push_str("• Distribution\n");
61            output.push_str("• Private use\n");
62            output.push('\n');
63            output.push_str("Requires:\n");
64            output.push_str("• License and copyright notice\n");
65            output.push('\n');
66            output.push_str("MIT License\n");
67            output.push('\n');
68            output.push_str(
69                "Permission is hereby granted, free of charge, to any person obtaining a copy\n",
70            );
71            output.push_str(
72                "of this software and associated documentation files (the \"Software\"), to deal\n",
73            );
74            output.push_str(
75                "in the Software without restriction, including without limitation the rights\n",
76            );
77            output.push_str(
78                "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n",
79            );
80            output.push_str(
81                "copies of the Software, and to permit persons to whom the Software is\n",
82            );
83            output.push_str("furnished to do so, subject to the following conditions:\n");
84            output.push('\n');
85            output.push_str(
86                "The above copyright notice and this permission notice shall be included in all\n",
87            );
88            output.push_str("copies or substantial portions of the Software.\n");
89            output.push('\n');
90            output.push_str(
91                "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
92            );
93            output.push_str(
94                "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
95            );
96            output.push_str(
97                "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n",
98            );
99            output.push_str(
100                "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
101            );
102            output.push_str(
103                "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n",
104            );
105            output.push_str(
106                "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n",
107            );
108            output.push_str("SOFTWARE.\n");
109        }
110        LicenseType::Apache2 => {
111            output.push_str("Apache License 2.0 - A permissive license that allows:\n");
112            output.push_str("• Commercial use\n");
113            output.push_str("• Modification\n");
114            output.push_str("• Distribution\n");
115            output.push_str("• Patent use\n");
116            output.push_str("• Private use\n");
117            output.push('\n');
118            output.push_str("Requires:\n");
119            output.push_str("• License and copyright notice\n");
120            output.push_str("• State changes\n");
121        }
122        LicenseType::CC0 => {
123            output.push_str("Creative Commons CC0 1.0 Universal - Public domain dedication:\n");
124            output.push_str("• No rights reserved\n");
125            output.push_str("• Can be used for any purpose\n");
126            output.push_str("• No attribution required\n");
127        }
128    }
129
130    output.push('\n');
131
132    if output::is_tty() {
133        use colored::Colorize;
134        use std::fmt::Write;
135        writeln!(
136            output,
137            "For full license text, see: {}",
138            "LICENSE file in project root".blue().underline()
139        )
140        .unwrap();
141    } else {
142        output.push_str("For full license text, see: LICENSE file in project root\n");
143    }
144
145    output
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn test_license_type_from_str() {
154        assert_eq!(LicenseType::parse("MIT"), Some(LicenseType::MIT));
155        assert_eq!(LicenseType::parse("mit"), Some(LicenseType::MIT));
156        assert_eq!(LicenseType::parse("Apache-2.0"), Some(LicenseType::Apache2));
157        assert_eq!(LicenseType::parse("apache"), Some(LicenseType::Apache2));
158        assert_eq!(LicenseType::parse("CC0-1.0"), Some(LicenseType::CC0));
159        assert_eq!(LicenseType::parse("cc0"), Some(LicenseType::CC0));
160        assert_eq!(LicenseType::parse("unknown"), None);
161    }
162
163    #[test]
164    fn test_license_type_name() {
165        assert_eq!(LicenseType::MIT.name(), "MIT");
166        assert_eq!(LicenseType::Apache2.name(), "Apache-2.0");
167        assert_eq!(LicenseType::CC0.name(), "CC0-1.0");
168    }
169
170    #[test]
171    fn test_display_license_mit() {
172        let output = display_license("test-tool", LicenseType::MIT);
173        assert!(output.contains("test-tool"));
174        assert!(output.contains("MIT"));
175        assert!(output.contains("Permission is hereby granted"));
176        assert!(output.contains("Commercial use"));
177    }
178
179    #[test]
180    fn test_display_license_apache() {
181        let output = display_license("test-tool", LicenseType::Apache2);
182        assert!(output.contains("test-tool"));
183        assert!(output.contains("Apache"));
184        assert!(output.contains("Patent use"));
185    }
186
187    #[test]
188    fn test_display_license_cc0() {
189        let output = display_license("test-tool", LicenseType::CC0);
190        assert!(output.contains("test-tool"));
191        assert!(output.contains("CC0"));
192        assert!(output.contains("No rights reserved"));
193    }
194}