use core::fmt;
pub const ASDF_STANDARD_TAG_PREFIX: &str = "tag:stsci.edu:asdf/";
pub const ASDF_CORE_TAG_PREFIX: &str = "tag:stsci.edu:asdf/core/";
pub const YAML_TAG_PREFIX: &str = "tag:yaml.org,2002:";
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tag {
handle: String,
suffix: String,
}
impl Tag {
pub fn new(handle: impl Into<String>, suffix: impl Into<String>) -> Self {
Self { handle: handle.into(), suffix: suffix.into() }
}
pub fn parse(full: &str) -> Self {
for prefix in [ASDF_STANDARD_TAG_PREFIX, YAML_TAG_PREFIX] {
if let Some(rest) = full.strip_prefix(prefix) {
return Self::new(prefix, rest);
}
}
Self::new(full, "")
}
pub fn handle(&self) -> &str {
&self.handle
}
pub fn suffix(&self) -> &str {
&self.suffix
}
pub fn full(&self) -> String {
format!("{}{}", self.handle, self.suffix)
}
pub fn is_asdf(&self) -> bool {
self.handle == ASDF_STANDARD_TAG_PREFIX || self.full().starts_with(ASDF_STANDARD_TAG_PREFIX)
}
pub fn is_yaml_builtin(&self) -> bool {
self.handle == YAML_TAG_PREFIX || self.full().starts_with(YAML_TAG_PREFIX)
}
pub fn asdf_name(&self) -> Option<&str> {
if self.handle == ASDF_STANDARD_TAG_PREFIX { Some(&self.suffix) } else { None }
}
pub fn split_version(&self) -> (&str, Option<&str>) {
let name = self.asdf_name().unwrap_or(&self.suffix);
split_tag_version(name)
}
}
pub fn split_tag_version(name: &str) -> (&str, Option<&str>) {
match name.rfind('-') {
Some(idx) => {
let (head, tail) = name.split_at(idx);
let version = &tail[1..];
if version.starts_with(|c: char| c.is_ascii_digit()) {
(head, Some(version))
} else {
(name, None)
}
}
None => (name, None),
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.handle, self.suffix)
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Tag({}{})", self.handle, self.suffix)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TagHandle {
pub handle: String,
pub prefix: String,
}
impl TagHandle {
pub fn asdf_default() -> Self {
Self { handle: "!".into(), prefix: ASDF_STANDARD_TAG_PREFIX.into() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_asdf_tag() {
let t = Tag::parse("tag:stsci.edu:asdf/core/ndarray-1.1.0");
assert_eq!(t.handle(), ASDF_STANDARD_TAG_PREFIX);
assert_eq!(t.suffix(), "core/ndarray-1.1.0");
assert!(t.is_asdf());
assert_eq!(t.asdf_name(), Some("core/ndarray-1.1.0"));
}
#[test]
fn splits_version() {
let t = Tag::parse("tag:stsci.edu:asdf/core/ndarray-1.1.0");
assert_eq!(t.split_version(), ("core/ndarray", Some("1.1.0")));
let t = Tag::parse("tag:stsci.edu:asdf/core/history_entry-1.0.0");
assert_eq!(t.split_version(), ("core/history_entry", Some("1.0.0")));
}
#[test]
fn hyphen_without_version_is_not_split() {
assert_eq!(split_tag_version("some-name"), ("some-name", None));
assert_eq!(split_tag_version("plain"), ("plain", None));
}
#[test]
fn recognises_yaml_builtins() {
let t = Tag::parse("tag:yaml.org,2002:int");
assert!(t.is_yaml_builtin());
assert!(!t.is_asdf());
assert_eq!(t.asdf_name(), None);
}
#[test]
fn full_round_trips() {
for s in ["tag:stsci.edu:asdf/core/asdf-1.1.0", "tag:yaml.org,2002:str", "!local"] {
assert_eq!(Tag::parse(s).full(), s);
}
}
}