Skip to content

Commit d646a0d

Browse files
committed
refactor: extract AnsibleHost and SshPrivateKeyFile types into separate modules
- Extract AnsibleHost wrapper type into ansible_host.rs module - Extract SshPrivateKeyFile wrapper type into ssh_private_key_file.rs module - Refactor inventory/mod.rs to import and re-export the types - Maintain all existing functionality and tests (13 tests still pass) - Improve code organization and maintainability through separation of concerns
1 parent a316db1 commit d646a0d

File tree

3 files changed

+123
-108
lines changed

3 files changed

+123
-108
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//! Ansible host wrapper type for IP address validation and serialization
2+
3+
use anyhow::{Context, Result};
4+
use serde::Serialize;
5+
use std::fmt;
6+
use std::net::IpAddr;
7+
use std::str::FromStr;
8+
9+
/// Wrapper type for Ansible host address using the newtype pattern
10+
///
11+
/// Ansible's `ansible_host` can contain:
12+
/// - Hostnames (e.g., "server.example.com")
13+
/// - FQDN (e.g., "www.example.com")
14+
/// - IP addresses (IPv4/IPv6)
15+
/// - Custom connection aliases
16+
/// - SSH proxy configurations
17+
///
18+
/// For this implementation, we only support IP addresses (IPv4 and IPv6) for simplicity.
19+
#[derive(Debug, Clone, PartialEq, Eq)]
20+
pub struct AnsibleHost(IpAddr);
21+
22+
impl AnsibleHost {
23+
/// Create a new `AnsibleHost` from an IP address
24+
#[must_use]
25+
pub fn new(ip: IpAddr) -> Self {
26+
Self(ip)
27+
}
28+
29+
/// Get the inner IP address
30+
#[must_use]
31+
pub fn as_ip_addr(&self) -> &IpAddr {
32+
&self.0
33+
}
34+
35+
/// Convert to string representation
36+
#[must_use]
37+
pub fn as_str(&self) -> String {
38+
self.0.to_string()
39+
}
40+
}
41+
42+
impl FromStr for AnsibleHost {
43+
type Err = anyhow::Error;
44+
45+
fn from_str(s: &str) -> Result<Self, Self::Err> {
46+
let ip = IpAddr::from_str(s).with_context(|| format!("Invalid IP address format: {s}"))?;
47+
Ok(Self(ip))
48+
}
49+
}
50+
51+
impl fmt::Display for AnsibleHost {
52+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53+
write!(f, "{}", self.0)
54+
}
55+
}
56+
57+
impl Serialize for AnsibleHost {
58+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59+
where
60+
S: serde::Serializer,
61+
{
62+
serializer.serialize_str(&self.0.to_string())
63+
}
64+
}

src/template/wrappers/ansible/inventory.rs renamed to src/template/wrappers/ansible/inventory/mod.rs

Lines changed: 5 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,122 +2,19 @@
22
//!
33
//! This template has mandatory variables that must be provided at construction time.
44
5+
pub mod ansible_host;
6+
pub mod ssh_private_key_file;
7+
58
use crate::template::file::File;
69
use crate::template::{StaticContext, TemplateRenderer};
710
use anyhow::{Context, Result};
811
use serde::Serialize;
9-
use std::fmt;
1012
use std::fs;
11-
use std::net::IpAddr;
1213
use std::path::Path;
1314
use std::str::FromStr;
1415

15-
/// Wrapper type for Ansible host address using the newtype pattern
16-
///
17-
/// Ansible's `ansible_host` can contain:
18-
/// - Hostnames (e.g., "server.example.com")
19-
/// - FQDN (e.g., "www.example.com")
20-
/// - IP addresses (IPv4/IPv6)
21-
/// - Custom connection aliases
22-
/// - SSH proxy configurations
23-
///
24-
/// For this implementation, we only support IP addresses (IPv4 and IPv6) for simplicity.
25-
#[derive(Debug, Clone, PartialEq, Eq)]
26-
pub struct AnsibleHost(IpAddr);
27-
28-
impl AnsibleHost {
29-
/// Create a new `AnsibleHost` from an IP address
30-
#[must_use]
31-
pub fn new(ip: IpAddr) -> Self {
32-
Self(ip)
33-
}
34-
35-
/// Get the inner IP address
36-
#[must_use]
37-
pub fn as_ip_addr(&self) -> &IpAddr {
38-
&self.0
39-
}
40-
41-
/// Convert to string representation
42-
#[must_use]
43-
pub fn as_str(&self) -> String {
44-
self.0.to_string()
45-
}
46-
}
47-
48-
impl FromStr for AnsibleHost {
49-
type Err = anyhow::Error;
50-
51-
fn from_str(s: &str) -> Result<Self, Self::Err> {
52-
let ip = IpAddr::from_str(s).with_context(|| format!("Invalid IP address format: {s}"))?;
53-
Ok(Self(ip))
54-
}
55-
}
56-
57-
impl fmt::Display for AnsibleHost {
58-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59-
write!(f, "{}", self.0)
60-
}
61-
}
62-
63-
impl Serialize for AnsibleHost {
64-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65-
where
66-
S: serde::Serializer,
67-
{
68-
serializer.serialize_str(&self.0.to_string())
69-
}
70-
}
71-
72-
/// Wrapper type for SSH private key file path using the newtype pattern
73-
#[derive(Debug, Clone, PartialEq, Eq)]
74-
pub struct SshPrivateKeyFile(String);
75-
76-
impl SshPrivateKeyFile {
77-
/// Create a new `SshPrivateKeyFile` from a string path
78-
pub fn new<S: Into<String>>(path: S) -> Self {
79-
Self(path.into())
80-
}
81-
82-
/// Get the inner path as a string reference
83-
#[must_use]
84-
pub fn as_str(&self) -> &str {
85-
&self.0
86-
}
87-
88-
/// Get the inner path as a string
89-
#[must_use]
90-
pub fn as_string(&self) -> String {
91-
self.0.clone()
92-
}
93-
}
94-
95-
impl From<&str> for SshPrivateKeyFile {
96-
fn from(path: &str) -> Self {
97-
Self::new(path)
98-
}
99-
}
100-
101-
impl From<String> for SshPrivateKeyFile {
102-
fn from(path: String) -> Self {
103-
Self(path)
104-
}
105-
}
106-
107-
impl fmt::Display for SshPrivateKeyFile {
108-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109-
write!(f, "{}", self.0)
110-
}
111-
}
112-
113-
impl Serialize for SshPrivateKeyFile {
114-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115-
where
116-
S: serde::Serializer,
117-
{
118-
serializer.serialize_str(&self.0)
119-
}
120-
}
16+
pub use ansible_host::AnsibleHost;
17+
pub use ssh_private_key_file::SshPrivateKeyFile;
12118

12219
#[derive(Debug)]
12320
pub struct InventoryTemplate {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! SSH private key file wrapper type for path handling and serialization
2+
3+
use serde::Serialize;
4+
use std::fmt;
5+
6+
/// Wrapper type for SSH private key file path using the newtype pattern
7+
#[derive(Debug, Clone, PartialEq, Eq)]
8+
pub struct SshPrivateKeyFile(String);
9+
10+
impl SshPrivateKeyFile {
11+
/// Create a new `SshPrivateKeyFile` from a string path
12+
pub fn new<S: Into<String>>(path: S) -> Self {
13+
Self(path.into())
14+
}
15+
16+
/// Get the inner path as a string reference
17+
#[must_use]
18+
pub fn as_str(&self) -> &str {
19+
&self.0
20+
}
21+
22+
/// Get the inner path as a string
23+
#[must_use]
24+
pub fn as_string(&self) -> String {
25+
self.0.clone()
26+
}
27+
}
28+
29+
impl From<&str> for SshPrivateKeyFile {
30+
fn from(path: &str) -> Self {
31+
Self::new(path)
32+
}
33+
}
34+
35+
impl From<String> for SshPrivateKeyFile {
36+
fn from(path: String) -> Self {
37+
Self(path)
38+
}
39+
}
40+
41+
impl fmt::Display for SshPrivateKeyFile {
42+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43+
write!(f, "{}", self.0)
44+
}
45+
}
46+
47+
impl Serialize for SshPrivateKeyFile {
48+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49+
where
50+
S: serde::Serializer,
51+
{
52+
serializer.serialize_str(&self.0)
53+
}
54+
}

0 commit comments

Comments
 (0)