Last active
January 19, 2025 03:10
-
-
Save Hoverbear/a329ca3ee21b08b78725bc84aed3f100 to your computer and use it in GitHub Desktop.
Snippet for Rust that lets you take a URL or path in a clap arg.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #[derive(clap::Parser, Debug)] | |
| pub(crate) struct CommandLineArguments { | |
| /// A git repo or path to use as the source | |
| #[clap(value_parser = clap::value_parser!(UrlOrPath))] | |
| source: Option<UrlOrPath>, | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{fmt::Display, str::FromStr}; | |
| use camino::Utf8PathBuf; | |
| use clap::error::{ContextKind, ContextValue}; | |
| use serde::{Deserialize, Serialize}; | |
| use url::Url; | |
| #[derive(Debug)] | |
| pub enum UrlOrPathError { | |
| Parsing(String) | |
| } | |
| impl error_stack::Context for UrlOrPathError {} | |
| impl std::fmt::Display for UrlOrPathError { | |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| match self { | |
| UrlOrPathError::Parsing(value) => f.write_fmt(format_args!("Error parsing `{value}` as URL or path")), | |
| } | |
| } | |
| } | |
| #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)] | |
| pub enum UrlOrPath { | |
| Url(Url), | |
| Path(Utf8PathBuf), | |
| } | |
| impl Display for UrlOrPath { | |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| match self { | |
| UrlOrPath::Url(url) => f.write_fmt(format_args!("{url}")), | |
| UrlOrPath::Path(path) => f.write_fmt(format_args!("{path}")), | |
| } | |
| } | |
| } | |
| impl FromStr for UrlOrPath { | |
| type Err = url::ParseError; | |
| fn from_str(s: &str) -> Result<Self, Self::Err> { | |
| match Url::parse(s) { | |
| Ok(url) => Ok(UrlOrPath::Url(url)), | |
| Err(url::ParseError::RelativeUrlWithoutBase) => { | |
| // This is most likely a relative path (`./boop` or `boop`) | |
| // or an absolute path (`/boop`) | |
| Ok(UrlOrPath::Path(Utf8PathBuf::from(s))) | |
| }, | |
| Err(e) => Err(e), | |
| } | |
| } | |
| } | |
| impl clap::builder::TypedValueParser for UrlOrPath { | |
| type Value = UrlOrPath; | |
| fn parse_ref( | |
| &self, | |
| cmd: &clap::Command, | |
| _arg: Option<&clap::Arg>, | |
| value: &std::ffi::OsStr, | |
| ) -> Result<Self::Value, clap::Error> { | |
| let value_str = value.to_str().ok_or_else(|| { | |
| let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue); | |
| err.insert( | |
| ContextKind::InvalidValue, | |
| ContextValue::String(format!("`{value:?}` not a UTF-8 string")), | |
| ); | |
| err | |
| })?; | |
| match UrlOrPath::from_str(value_str) { | |
| Ok(v) => Ok(v), | |
| Err(from_str_error) => { | |
| let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue).with_cmd(cmd); | |
| err.insert( | |
| clap::error::ContextKind::Custom, | |
| clap::error::ContextValue::String(from_str_error.to_string()), | |
| ); | |
| Err(err) | |
| }, | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment