ipfrs_cli/utils.rs
1//! Utility functions for CLI maintenance and distribution
2//!
3//! This module provides utilities for:
4//! - Man page generation
5//! - Auto-update checking
6//! - Version management
7
8use anyhow::{Context, Result};
9use std::path::Path;
10
11/// Current version of the CLI
12pub const VERSION: &str = env!("CARGO_PKG_VERSION");
13
14/// Repository URL for checking updates
15pub const REPO_URL: &str = "https://github.com/tensorlogic/ipfrs";
16
17/// Generate man pages for all commands
18///
19/// This function generates comprehensive man pages using clap_mangen.
20/// The man pages are written to the specified output directory.
21///
22/// # Arguments
23///
24/// * `cmd` - The clap::Command structure to generate man pages for
25/// * `out_dir` - Directory where man pages will be written
26///
27/// # Examples
28///
29/// ```no_run
30/// use ipfrs_cli::{build_cli, utils::generate_man_pages};
31/// use std::path::Path;
32///
33/// let cmd = build_cli();
34/// let out_dir = Path::new("target/man");
35/// generate_man_pages(&cmd, out_dir).expect("Failed to generate man pages");
36/// ```
37#[allow(dead_code)]
38pub fn generate_man_pages(cmd: &clap::Command, out_dir: &Path) -> Result<()> {
39 use clap_mangen::Man;
40 use std::fs;
41
42 // Ensure output directory exists
43 fs::create_dir_all(out_dir).context("Failed to create output directory")?;
44
45 // Generate main man page
46 let man = Man::new(cmd.clone());
47 let mut buffer = Vec::new();
48 man.render(&mut buffer)
49 .context("Failed to render main man page")?;
50
51 let main_path = out_dir.join("ipfrs.1");
52 fs::write(&main_path, buffer).context("Failed to write main man page")?;
53
54 println!("Generated: {}", main_path.display());
55
56 // Generate man pages for each subcommand
57 for subcommand in cmd.get_subcommands() {
58 let name = subcommand.get_name();
59 let man = Man::new(subcommand.clone());
60 let mut buffer = Vec::new();
61 man.render(&mut buffer)
62 .with_context(|| format!("Failed to render man page for {}", name))?;
63
64 let path = out_dir.join(format!("ipfrs-{}.1", name));
65 fs::write(&path, buffer)
66 .with_context(|| format!("Failed to write man page for {}", name))?;
67
68 println!("Generated: {}", path.display());
69 }
70
71 Ok(())
72}
73
74/// Check for available updates
75///
76/// This function checks if a newer version is available by querying
77/// the GitHub releases API.
78///
79/// # Returns
80///
81/// Returns `Some(version)` if an update is available, `None` otherwise.
82///
83/// # Examples
84///
85/// ```no_run
86/// use ipfrs_cli::utils::check_for_updates;
87///
88/// # async fn example() {
89/// match check_for_updates().await {
90/// Ok(Some(version)) => println!("Update available: {}", version),
91/// Ok(None) => println!("Up to date"),
92/// Err(e) => eprintln!("Failed to check for updates: {}", e),
93/// }
94/// # }
95/// ```
96pub async fn check_for_updates() -> Result<Option<String>> {
97 // For now, this is a placeholder implementation
98 // In a real implementation, this would query GitHub API or similar
99 Ok(None)
100}
101
102/// Compare two semantic versions
103///
104/// Returns true if `new_version` is newer than `current_version`.
105#[allow(dead_code)]
106fn is_newer_version(current_version: &str, new_version: &str) -> bool {
107 // Simple string comparison for now
108 // In production, use a proper semver crate
109 current_version < new_version
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 #[allow(clippy::len_zero)]
118 fn test_version_constant() {
119 // VERSION is a non-empty constant from CARGO_PKG_VERSION
120 assert!(VERSION.len() > 0);
121 assert!(VERSION.contains('.'));
122 }
123
124 #[test]
125 fn test_repo_url() {
126 assert!(REPO_URL.starts_with("https://"));
127 }
128
129 #[test]
130 fn test_is_newer_version() {
131 assert!(is_newer_version("0.1.0", "0.2.0"));
132 assert!(is_newer_version("0.1.0", "1.0.0"));
133 assert!(!is_newer_version("0.2.0", "0.1.0"));
134 assert!(!is_newer_version("1.0.0", "1.0.0"));
135 }
136}