Parse Claude auth input. Accepted formats: - plain code (`abc123`) - URL/query with `code=` - `code#state` (OpenCode-style)
(input: &str)
| 568 | /// - URL/query with `code=` |
| 569 | /// - `code#state` (OpenCode-style) |
| 570 | fn parse_claude_code_input(input: &str) -> Result<(String, Option<String>)> { |
| 571 | let trimmed = input.trim(); |
| 572 | |
| 573 | let (raw_code, state_from_query) = if trimmed.contains("code=") { |
| 574 | let url = url::Url::parse(trimmed) |
| 575 | .or_else(|_| url::Url::parse(&format!("https://example.com?{}", trimmed)))?; |
| 576 | let code = url |
| 577 | .query_pairs() |
| 578 | .find(|(k, _)| k == "code") |
| 579 | .map(|(_, v)| v.to_string()) |
| 580 | .ok_or_else(|| anyhow::anyhow!("No code found in URL"))?; |
| 581 | let state = url |
| 582 | .query_pairs() |
| 583 | .find(|(k, _)| k == "state") |
| 584 | .map(|(_, v)| v.to_string()); |
| 585 | (code, state) |
| 586 | } else { |
| 587 | (trimmed.to_string(), None) |
| 588 | }; |
| 589 | |
| 590 | let (code, state) = if raw_code.contains('#') { |
| 591 | let parts: Vec<&str> = raw_code.splitn(2, '#').collect(); |
| 592 | (parts[0].to_string(), Some(parts[1].to_string())) |
| 593 | } else { |
| 594 | (raw_code, state_from_query) |
| 595 | }; |
| 596 | |
| 597 | if code.trim().is_empty() { |
| 598 | anyhow::bail!("No authorization code provided"); |
| 599 | } |
| 600 | |
| 601 | Ok((code, state)) |
| 602 | } |
| 603 | |
| 604 | pub fn claude_redirect_uri_for_input(input: &str, fallback_redirect_uri: &str) -> String { |
| 605 | let trimmed = input.trim(); |