transfer_family_cli 0.3.0

TUI to browse and transfer files via AWS Transfer Family connector
Documentation
//! Domain newtypes for string-like values (connector ID, remote path, transfer ID, etc.).

use std::fmt;

/// AWS Transfer Family connector ID (e.g. `c-01234567890abcdef`).
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ConnectorId(String);

impl ConnectorId {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for ConnectorId {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl From<&str> for ConnectorId {
    fn from(s: &str) -> Self {
        Self(s.to_string())
    }
}

impl AsRef<str> for ConnectorId {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for ConnectorId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// SFTP-style remote path (e.g. `/foo/bar.txt`, `/`).
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RemotePath(String);

impl RemotePath {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }

    #[must_use]
    pub fn is_root(&self) -> bool {
        let p = self.0.trim_matches('/');
        p.is_empty()
    }

    /// Returns the parent path (e.g. `/foo/` from `/foo/bar/`).
    #[must_use]
    pub fn parent(&self) -> RemotePath {
        let p = self.0.trim_matches('/');
        if p.is_empty() {
            return RemotePath("/".to_string());
        }
        let mut parts: Vec<&str> = p.split('/').filter(|s| !s.is_empty()).collect();
        parts.pop();
        if parts.is_empty() {
            RemotePath("/".to_string())
        } else {
            RemotePath(format!("/{}/", parts.join("/")))
        }
    }

    /// Joins a segment to this path (handles trailing/leading slashes).
    #[must_use]
    pub fn join(&self, segment: &str) -> RemotePath {
        let base = self.0.trim_end_matches('/');
        let rest = segment.trim_start_matches('/').trim_end_matches('/');
        if base.is_empty() {
            RemotePath(format!("/{rest}"))
        } else if rest.is_empty() {
            RemotePath(format!("/{base}"))
        } else {
            RemotePath(format!("/{base}/{rest}"))
        }
    }

    /// Resolves a path (absolute or relative) against this path.
    #[must_use]
    pub fn resolve(&self, path: &str) -> RemotePath {
        let path = path.trim();
        if path.starts_with('/') {
            return RemotePath(path.to_string());
        }
        self.join(path)
    }
}

impl From<String> for RemotePath {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl From<&str> for RemotePath {
    fn from(s: &str) -> Self {
        Self(s.to_string())
    }
}

impl AsRef<str> for RemotePath {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RemotePath {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// ID returned from `StartFileTransfer`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TransferId(String);

impl TransferId {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for TransferId {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl AsRef<str> for TransferId {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for TransferId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// ID returned from `StartDirectoryListing`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ListingId(String);

impl ListingId {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for ListingId {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl AsRef<str> for ListingId {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for ListingId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Listing result filename in S3 (from `StartDirectoryListing`).
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OutputFileName(String);

impl OutputFileName {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for OutputFileName {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl AsRef<str> for OutputFileName {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for OutputFileName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// S3 bucket name.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct S3Bucket(String);

impl S3Bucket {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for S3Bucket {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl AsRef<str> for S3Bucket {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for S3Bucket {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// S3 object key.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct S3Key(String);

impl S3Key {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for S3Key {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl AsRef<str> for S3Key {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for S3Key {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// S3 root path "bucket/prefix" for transfer CLI data.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct S3Root(String);

impl S3Root {
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<String> for S3Root {
    fn from(s: String) -> Self {
        Self(s)
    }
}

impl From<&str> for S3Root {
    fn from(s: &str) -> Self {
        Self(s.to_string())
    }
}

impl AsRef<str> for S3Root {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for S3Root {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}