bhcli

A TUI for chatting on LE PHP Chats
git clone https://git.dasho.dev/bhcli.git
Log | Files | Refs | README

doc.rs (13337B)


      1 use crate::chatops::{ChatCommand, ChatOpError, ChatOpResult, CommandContext};
      2 use std::process::Command;
      3 
      4 /// Manual page lookup command
      5 pub struct ManCommand;
      6 
      7 impl ChatCommand for ManCommand {
      8    fn name(&self) -> &'static str {
      9        "man"
     10    }
     11    fn description(&self) -> &'static str {
     12        "Get manual page summary for a command"
     13    }
     14    fn usage(&self) -> &'static str {
     15        "/man <command>"
     16    }
     17 
     18    fn execute(
     19        &self,
     20        args: Vec<String>,
     21        _context: &CommandContext,
     22    ) -> Result<ChatOpResult, ChatOpError> {
     23        if args.is_empty() {
     24            return Err(ChatOpError::MissingArguments(
     25                "Please specify a command".to_string(),
     26            ));
     27        }
     28 
     29        let command = &args[0];
     30 
     31        // Try to get man page using `man` command
     32        let match_result = match Command::new("man")
     33            .args(&["-f", command]) // whatis format - brief description
     34            .output()
     35        {
     36            Ok(output) => {
     37                if output.status.success() {
     38                    let result = String::from_utf8_lossy(&output.stdout);
     39                    if result.trim().is_empty() {
     40                        ChatOpResult::Message(format!("📖 No manual entry found for '{}'", command))
     41                    } else {
     42                        let lines: Vec<String> = result
     43                            .lines()
     44                            .take(5) // Limit to first 5 results
     45                            .map(|line| format!("📖 {}", line.trim()))
     46                            .collect();
     47                        ChatOpResult::Block(lines)
     48                    }
     49                } else {
     50                    ChatOpResult::Message(format!("📖 No manual entry found for '{}'", command))
     51                }
     52            }
     53            Err(_) => {
     54                // Fallback with some common commands
     55                let description = match command.as_str() {
     56                    "curl" => "transfer a URL - command line tool for transferring data",
     57                    "grep" => "print lines that match patterns",
     58                    "awk" => "pattern scanning and processing language",
     59                    "sed" => "stream editor for filtering and transforming text",
     60                    "tmux" => "terminal multiplexer",
     61                    "ssh" => "OpenSSH SSH client (remote login program)",
     62                    "git" => "the stupid content tracker",
     63                    "docker" => "A self-sufficient runtime for containers",
     64                    "vim" => "Vi IMproved, a programmer's text editor",
     65                    "ls" => "list directory contents",
     66                    "cd" => "change directory",
     67                    "cat" => "concatenate files and print on the standard output",
     68                    "tail" => "output the last part of files",
     69                    "head" => "output the first part of files",
     70                    _ => {
     71                        return Ok(ChatOpResult::Error(format!(
     72                            "No manual entry found for '{}' and man command not available",
     73                            command
     74                        )))
     75                    }
     76                };
     77                ChatOpResult::Message(format!("📖 {} - {}", command, description))
     78            }
     79        };
     80        Ok(match_result)
     81    }
     82 }
     83 
     84 /// Language-specific documentation lookup
     85 pub struct DocCommand;
     86 
     87 impl ChatCommand for DocCommand {
     88    fn name(&self) -> &'static str {
     89        "doc"
     90    }
     91    fn description(&self) -> &'static str {
     92        "Get language-specific documentation"
     93    }
     94    fn usage(&self) -> &'static str {
     95        "/doc <language> <term>"
     96    }
     97 
     98    fn execute(
     99        &self,
    100        args: Vec<String>,
    101        _context: &CommandContext,
    102    ) -> Result<ChatOpResult, ChatOpError> {
    103        if args.len() < 2 {
    104            return Err(ChatOpError::MissingArguments(
    105                "Please specify language and term".to_string(),
    106            ));
    107        }
    108 
    109        let language = args[0].to_lowercase();
    110        let term = &args[1];
    111 
    112        let doc_url = match language.as_str() {
    113            "rust" => format!("https://doc.rust-lang.org/std/?search={}", term),
    114            "python" | "py" => format!("https://docs.python.org/3/search.html?q={}", term),
    115            "javascript" | "js" => format!("https://developer.mozilla.org/en-US/search?q={}", term),
    116            "c" => format!("https://en.cppreference.com/w/c?search={}", term),
    117            "cpp" | "c++" => format!("https://en.cppreference.com/w/cpp?search={}", term),
    118            "go" => format!("https://pkg.go.dev/search?q={}", term),
    119            "java" => format!(
    120                "https://docs.oracle.com/en/java/javase/17/docs/api/index.html?search={}",
    121                term
    122            ),
    123            _ => {
    124                return Err(ChatOpError::InvalidSyntax(format!(
    125                    "Unsupported language: {}",
    126                    language
    127                )))
    128            }
    129        };
    130 
    131        Ok(ChatOpResult::Message(format!(
    132            "📚 {} docs for '{}': {}",
    133            language, term, doc_url
    134        )))
    135    }
    136 }
    137 
    138 /// Concept explanation command
    139 pub struct ExplainCommand;
    140 
    141 impl ChatCommand for ExplainCommand {
    142    fn name(&self) -> &'static str {
    143        "explain"
    144    }
    145    fn description(&self) -> &'static str {
    146        "Explain programming concepts"
    147    }
    148    fn usage(&self) -> &'static str {
    149        "/explain <concept>"
    150    }
    151 
    152    fn execute(
    153        &self,
    154        args: Vec<String>,
    155        _context: &CommandContext,
    156    ) -> Result<ChatOpResult, ChatOpError> {
    157        if args.is_empty() {
    158            return Err(ChatOpError::MissingArguments(
    159                "Please specify a concept to explain".to_string(),
    160            ));
    161        }
    162 
    163        let concept = args.join(" ").to_lowercase();
    164 
    165        let explanation = match concept.as_str() {
    166            "async" | "asynchronous" => {
    167                "🔄 **Asynchronous Programming**: A programming paradigm that allows code to run concurrently without blocking. Operations can be initiated and then the program can continue executing other code while waiting for the operation to complete."
    168            }
    169            "regex" | "regular expression" => {
    170                "🔍 **Regular Expressions**: Patterns used to match character combinations in strings. Useful for searching, extracting, and replacing text based on patterns."
    171            }
    172            "jwt" | "json web token" => {
    173                "🔐 **JWT (JSON Web Token)**: A compact, URL-safe means of representing claims between two parties. Used for authentication and secure information transmission."
    174            }
    175            "rest api" | "restful" => {
    176                "🌐 **REST API**: Representational State Transfer - an architectural style for designing networked applications using standard HTTP methods (GET, POST, PUT, DELETE)."
    177            }
    178            "docker" => {
    179                "🐳 **Docker**: A containerization platform that packages applications with their dependencies into lightweight, portable containers."
    180            }
    181            "git" => {
    182                "📝 **Git**: A distributed version control system for tracking changes in source code during software development."
    183            }
    184            "mutex" | "mutual exclusion" => {
    185                "🔒 **Mutex**: A synchronization primitive that prevents multiple threads from accessing shared data simultaneously, preventing race conditions."
    186            }
    187            "blockchain" => {
    188                "⛓️ **Blockchain**: A distributed ledger technology that maintains a continuously growing list of records (blocks) linked using cryptography."
    189            }
    190            _ => {
    191                return Ok(ChatOpResult::Message(format!("🤔 I don't have an explanation for '{}' yet. Try searching online or ask a more specific question.", concept)));
    192            }
    193        };
    194 
    195        Ok(ChatOpResult::Message(explanation.to_string()))
    196    }
    197 }
    198 
    199 /// Cheat sheet lookup command
    200 pub struct CheatCommand;
    201 
    202 impl ChatCommand for CheatCommand {
    203    fn name(&self) -> &'static str {
    204        "cheat"
    205    }
    206    fn description(&self) -> &'static str {
    207        "Get cheat sheets from cht.sh"
    208    }
    209    fn usage(&self) -> &'static str {
    210        "/cheat <term>"
    211    }
    212 
    213    fn execute(
    214        &self,
    215        args: Vec<String>,
    216        _context: &CommandContext,
    217    ) -> Result<ChatOpResult, ChatOpError> {
    218        if args.is_empty() {
    219            return Err(ChatOpError::MissingArguments(
    220                "Please specify a term for the cheat sheet".to_string(),
    221            ));
    222        }
    223 
    224        let term = args.join("+");
    225 
    226        // Try to fetch from cht.sh with plain text output and timeout
    227        match Command::new("curl")
    228            .args(&[
    229                "-s",
    230                "--max-time",
    231                "5",
    232                &format!("https://cht.sh/{}?T", term),
    233            ])
    234            .output()
    235        {
    236            Ok(output) => {
    237                if output.status.success() {
    238                    let result = String::from_utf8_lossy(&output.stdout);
    239                    let lines: Vec<String> = result
    240                        .lines()
    241                        .take(20) // Limit to prevent spam but allow context
    242                        .map(|line| line.to_string())
    243                        .collect();
    244 
    245                    if lines.is_empty() || lines[0].contains("Unknown") {
    246                        Ok(ChatOpResult::Message(format!(
    247                            "📋 No cheat sheet found for '{}'",
    248                            args.join(" ")
    249                        )))
    250                    } else {
    251                        Ok(ChatOpResult::CodeBlock(
    252                            lines.join("\n"),
    253                            Some("text".to_string()),
    254                        ))
    255                    }
    256                } else {
    257                    Err(ChatOpError::NetworkError(
    258                        "Failed to fetch cheat sheet".to_string(),
    259                    ))
    260                }
    261            }
    262            Err(_) => {
    263                // Fallback message
    264                Ok(ChatOpResult::Message(format!(
    265                    "📋 Try: https://cht.sh/{}",
    266                    term
    267                )))
    268            }
    269        }
    270    }
    271 }
    272 
    273 /// StackOverflow search command
    274 pub struct StackOverflowCommand;
    275 
    276 impl ChatCommand for StackOverflowCommand {
    277    fn name(&self) -> &'static str {
    278        "so"
    279    }
    280    fn description(&self) -> &'static str {
    281        "Search StackOverflow"
    282    }
    283    fn usage(&self) -> &'static str {
    284        "/so <query>"
    285    }
    286    fn aliases(&self) -> Vec<&'static str> {
    287        vec!["stackoverflow"]
    288    }
    289 
    290    fn execute(
    291        &self,
    292        args: Vec<String>,
    293        _context: &CommandContext,
    294    ) -> Result<ChatOpResult, ChatOpError> {
    295        if args.is_empty() {
    296            return Err(ChatOpError::MissingArguments(
    297                "Please specify a search query".to_string(),
    298            ));
    299        }
    300 
    301        let query = args.join(" ");
    302        let encoded_query = query.replace(" ", "+");
    303        let url = format!("https://stackoverflow.com/search?q={}", encoded_query);
    304 
    305        Ok(ChatOpResult::Message(format!(
    306            "🟠 StackOverflow search for '{}': {}",
    307            query, url
    308        )))
    309    }
    310 }
    311 
    312 /// Reference links command
    313 pub struct RefCommand;
    314 
    315 impl ChatCommand for RefCommand {
    316    fn name(&self) -> &'static str {
    317        "ref"
    318    }
    319    fn description(&self) -> &'static str {
    320        "Get official reference links"
    321    }
    322    fn usage(&self) -> &'static str {
    323        "/ref <library/language>"
    324    }
    325 
    326    fn execute(
    327        &self,
    328        args: Vec<String>,
    329        _context: &CommandContext,
    330    ) -> Result<ChatOpResult, ChatOpError> {
    331        if args.is_empty() {
    332            return Err(ChatOpError::MissingArguments(
    333                "Please specify a library or language".to_string(),
    334            ));
    335        }
    336 
    337        let library = args[0].to_lowercase();
    338 
    339        let reference = match library.as_str() {
    340            "rust" => ("🦀 Rust", "https://doc.rust-lang.org/std/"),
    341            "python" => ("🐍 Python", "https://docs.python.org/3/"),
    342            "javascript" | "js" => (
    343                "📜 JavaScript",
    344                "https://developer.mozilla.org/en-US/docs/Web/JavaScript",
    345            ),
    346            "react" => ("⚛️ React", "https://reactjs.org/docs/"),
    347            "vue" => ("💚 Vue.js", "https://vuejs.org/guide/"),
    348            "node" | "nodejs" => ("🟢 Node.js", "https://nodejs.org/en/docs/"),
    349            "docker" => ("🐳 Docker", "https://docs.docker.com/"),
    350            "git" => ("📝 Git", "https://git-scm.com/docs"),
    351            "linux" => ("🐧 Linux", "https://man7.org/linux/man-pages/"),
    352            "postgresql" | "postgres" => ("🐘 PostgreSQL", "https://www.postgresql.org/docs/"),
    353            "mysql" => ("🐬 MySQL", "https://dev.mysql.com/doc/"),
    354            "redis" => ("🔴 Redis", "https://redis.io/documentation"),
    355            _ => {
    356                return Ok(ChatOpResult::Message(format!(
    357                    "🔍 No reference found for '{}'. Try a web search instead.",
    358                    library
    359                )))
    360            }
    361        };
    362 
    363        Ok(ChatOpResult::Message(format!(
    364            "{} Reference: {}",
    365            reference.0, reference.1
    366        )))
    367    }
    368 }