use crate::{Pattern, PatternError, PkgPath, PkgPathError};
use std::fmt;
use std::str::FromStr;
use thiserror::Error;
#[cfg(feature = "serde")]
use serde_with::{DeserializeFromStr, SerializeDisplay};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(SerializeDisplay, DeserializeFromStr))]
pub struct Depend {
pattern: Pattern,
pkgpath: PkgPath,
}
impl Depend {
pub fn new(s: &str) -> Result<Self, DependError> {
let Some((left, right)) = s.split_once(':') else {
return Err(DependError::Invalid);
};
if right.contains(':') {
return Err(DependError::Invalid);
}
let pattern = Pattern::new(left)?;
let pkgpath = PkgPath::from_str(right)?;
Ok(Depend { pattern, pkgpath })
}
#[must_use]
pub fn pattern(&self) -> &Pattern {
&self.pattern
}
#[must_use]
pub fn pkgpath(&self) -> &PkgPath {
&self.pkgpath
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DependType {
#[default]
Full,
Build,
Bootstrap,
Tool,
Test,
}
#[derive(Debug, Error)]
pub enum DependError {
#[error("Invalid DEPENDS string")]
Invalid,
#[error(transparent)]
Pattern(#[from] PatternError),
#[error(transparent)]
PkgPath(#[from] PkgPathError),
}
impl fmt::Display for Depend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:../../{}", self.pattern, self.pkgpath)
}
}
impl FromStr for Depend {
type Err = DependError;
fn from_str(s: &str) -> Result<Self, DependError> {
Depend::new(s)
}
}
impl crate::kv::FromKv for Depend {
fn from_kv(value: &str, span: crate::kv::Span) -> crate::kv::Result<Self> {
Self::new(value).map_err(|e| crate::kv::KvError::Parse {
message: e.to_string(),
span,
})
}
}
impl TryFrom<&str> for Depend {
type Error = DependError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::new(s)
}
}
impl TryFrom<crate::scanindex::RawDepend<'_>> for Depend {
type Error = DependError;
fn try_from(
raw: crate::scanindex::RawDepend<'_>,
) -> Result<Self, Self::Error> {
Self::new(raw.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_good() -> Result<(), DependError> {
let pkgmatch = Pattern::new("mktools-[0-9]")?;
let pkgpath = PkgPath::new("../../pkgtools/mktools")?;
let dep = Depend::new("mktools-[0-9]:../../pkgtools/mktools")?;
assert_eq!(dep.pattern(), &pkgmatch);
assert_eq!(dep.pkgpath(), &pkgpath);
let dep = Depend::new("mktools-[0-9]:pkgtools/mktools")?;
assert_eq!(dep.pattern(), &pkgmatch);
assert_eq!(dep.pkgpath(), &pkgpath);
Ok(())
}
#[test]
fn test_bad() {
let dep = Depend::new("pkg");
assert!(matches!(dep, Err(DependError::Invalid)));
let dep = Depend::new("pkg-[0-9]*::../../pkgtools/pkg");
assert!(matches!(dep, Err(DependError::Invalid)));
let dep = Depend::new("pkg>2>3:../../pkgtools/pkg");
assert!(matches!(dep, Err(DependError::Pattern(_))));
let dep = Depend::new("ojnk:foo");
assert!(matches!(dep, Err(DependError::PkgPath(_))));
}
#[test]
fn test_display() -> Result<(), DependError> {
let dep = Depend::new("mktool-[0-9]*:../../pkgtools/mktool")?;
assert_eq!(dep.to_string(), "mktool-[0-9]*:../../pkgtools/mktool");
Ok(())
}
#[test]
fn test_from_str() -> Result<(), DependError> {
use std::str::FromStr;
let dep = Depend::from_str("mktool-[0-9]*:../../pkgtools/mktool")?;
assert_eq!(dep.pattern(), &Pattern::new("mktool-[0-9]*")?);
let dep: Depend = "pkg>=1.0:cat/pkg".parse()?;
assert_eq!(dep.pkgpath(), &PkgPath::new("cat/pkg")?);
let dep: Depend = "foo-[0-9]*:cat/foo".try_into()?;
assert!(dep.pattern().matches("foo-1.0"));
Ok(())
}
}