pub struct SshConfigFile {
pub elements: Vec<ConfigElement>,
pub path: PathBuf,
pub crlf: bool,
pub bom: bool,
}Expand description
Represents the entire SSH config file as a sequence of elements. Preserves the original structure for round-trip fidelity.
Fields§
§elements: Vec<ConfigElement>§path: PathBuf§crlf: boolWhether the original file used CRLF line endings.
bom: boolWhether the original file started with a UTF-8 BOM.
Implementations§
Source§impl SshConfigFile
impl SshConfigFile
Sourcepub fn host_entries(&self) -> Vec<HostEntry>
pub fn host_entries(&self) -> Vec<HostEntry>
Get all host entries as convenience views (including from Include files).
Pattern-inherited directives (ProxyJump, User, IdentityFile) are merged
using SSH-faithful alias-only matching so indicators like ↗ reflect what
SSH will actually apply when connecting via ssh <alias>.
Sourcepub fn raw_host_entry(&self, alias: &str) -> Option<HostEntry>
pub fn raw_host_entry(&self, alias: &str) -> Option<HostEntry>
Get a single host entry by alias without pattern inheritance applied. Returns the raw directives from the host’s own block only. Used by the edit form so inherited values can be shown as dimmed placeholders.
Sourcepub fn inherited_hints(&self, alias: &str) -> InheritedHints
pub fn inherited_hints(&self, alias: &str) -> InheritedHints
Compute pattern-provided field hints for a host alias. Returns first-match values and their source patterns for ProxyJump, User and IdentityFile. These are returned regardless of whether the host has its own values for those fields. The caller (form rendering) decides visibility based on whether the field is empty. Matches by alias only (SSH-faithful).
Sourcepub fn pattern_entries(&self) -> Vec<PatternEntry>
pub fn pattern_entries(&self) -> Vec<PatternEntry>
Get all pattern entries as convenience views (including from Include files).
Sourcepub fn matching_patterns(&self, alias: &str) -> Vec<PatternEntry>
pub fn matching_patterns(&self, alias: &str) -> Vec<PatternEntry>
Find all pattern blocks that match a given host alias and hostname. Returns entries in config order (first match first).
Sourcepub fn include_paths(&self) -> Vec<PathBuf>
pub fn include_paths(&self) -> Vec<PathBuf>
Collect all resolved Include file paths (recursively).
Sourcepub fn include_glob_dirs(&self) -> Vec<PathBuf>
pub fn include_glob_dirs(&self) -> Vec<PathBuf>
Collect parent directories of Include glob patterns. When a file is added/removed under a glob dir, the directory’s mtime changes.
Sourcepub fn has_host(&self, alias: &str) -> bool
pub fn has_host(&self, alias: &str) -> bool
Check if a host alias already exists (including in Include files). Walks the element tree directly without building HostEntry structs.
Sourcepub fn siblings_of(&self, alias: &str) -> Vec<String>
pub fn siblings_of(&self, alias: &str) -> Vec<String>
Return the sibling aliases that share a Host block with alias.
An empty vector means alias lives in its own single-alias block (or
is not present). A non-empty vector lists the other tokens in the
block in source order, so the UI can render indicators like +N or
spell the aliases out in a confirm dialog before a destructive
action. Does not recurse into Included files: those are read-only
and their hosts cannot be edited from purple anyway.
Sourcepub fn has_host_block(&self, pattern: &str) -> bool
pub fn has_host_block(&self, pattern: &str) -> bool
Check if a host block with exactly this host_pattern exists (top-level only).
Unlike has_host which splits multi-host patterns and checks individual parts,
this matches the full Host line pattern string (e.g. “web-* db-*”).
Does not search Include files (patterns from includes are read-only).
Sourcepub fn is_included_host(&self, alias: &str) -> bool
pub fn is_included_host(&self, alias: &str) -> bool
Check if a host alias is from an included file (read-only). Handles multi-pattern Host lines by splitting on whitespace.
Sourcepub fn add_host(&mut self, entry: &HostEntry)
pub fn add_host(&mut self, entry: &HostEntry)
Add a new host entry to the config.
Inserts before any trailing wildcard/pattern Host blocks (e.g. Host *)
so that SSH “first match wins” semantics are preserved. If wildcards are
only at the top of the file (acting as global defaults), appends at end.
Sourcepub fn last_element_has_trailing_blank(&self) -> bool
pub fn last_element_has_trailing_blank(&self) -> bool
Check if the last element already ends with a blank line.
Sourcepub fn update_host(&mut self, old_alias: &str, entry: &HostEntry)
pub fn update_host(&mut self, old_alias: &str, entry: &HostEntry)
Update an existing host entry by alias. Merges changes into the existing block, preserving unknown directives.
Alias matching uses whitespace-tokenized equality, so a host visible
under a multi-alias block like Host web-01 web-01.prod is reachable
from any of its aliases. Directives are shared across all tokens in
the block (per SSH semantics): updating User on web-01.prod
therefore also affects web-01.
On rename of a multi-alias block only the matching token is replaced
in the Host line; sibling aliases are preserved verbatim.
Sourcepub fn set_host_provider_id(
&mut self,
alias: &str,
id: &ProviderConfigId,
server_id: &str,
) -> bool
pub fn set_host_provider_id( &mut self, alias: &str, id: &ProviderConfigId, server_id: &str, ) -> bool
Set provider on a host block by alias using a full ProviderConfigId. Emits a 3-segment marker when the id has a label, 2-segment otherwise.
Refuses pattern aliases and multi-alias blocks: claiming a sibling alias as provider-owned cascades into stale-marking and bulk-purge, which would silently delete the user’s hand-curated entries.
Sourcepub fn rewrite_legacy_markers_to_label(
&mut self,
provider_name: &str,
label: &str,
) -> usize
pub fn rewrite_legacy_markers_to_label( &mut self, provider_name: &str, label: &str, ) -> usize
Rewrite every 2-segment legacy marker for provider_name to a
3-segment marker keyed to (provider_name, label). Used by the
lazy-migration flow so existing hosts of a now-labeled config stay
owned (and don’t get re-claimed or stale-marked) on the next sync.
Only top-level host blocks are rewritten; Include files are read-only per the project’s invariant. Returns the count of host blocks touched.
Sourcepub fn find_hosts_by_provider(
&self,
provider_name: &str,
) -> Vec<(String, String)>
pub fn find_hosts_by_provider( &self, provider_name: &str, ) -> Vec<(String, String)>
Find all hosts with a specific provider, returning (alias, server_id) pairs. Searches both top-level elements and Include files so that provider hosts in included configs are recognized during sync (prevents duplicate additions).
Sourcepub fn find_hosts_by_id(&self, id: &ProviderConfigId) -> Vec<(String, String)>
pub fn find_hosts_by_id(&self, id: &ProviderConfigId) -> Vec<(String, String)>
Find hosts owned by an exact ProviderConfigId. Used during multi-config sync
so two labeled configs of the same provider don’t claim each other’s hosts.
Legacy 2-segment markers match a bare id (label=None) for backward compatibility.
Sourcepub fn find_hosts_by_provider_raw(
&self,
provider_name: &str,
) -> Vec<(String, String)>
pub fn find_hosts_by_provider_raw( &self, provider_name: &str, ) -> Vec<(String, String)>
Like find_hosts_by_provider, but returns the FULL server_id from the
raw marker (everything after the first colon), without trying to
interpret the middle segment as a label. Used by sync of BARE configs
so server_ids containing colons (Proxmox qemu:300) are matched
against the API response one-to-one instead of being mis-parsed as
labeled markers.
Sourcepub fn add_forward(&mut self, alias: &str, directive_key: &str, value: &str)
pub fn add_forward(&mut self, alias: &str, directive_key: &str, value: &str)
Add a forwarding directive to a host block.
Inserts at content_end() (before trailing blanks), using detected indentation.
Uses split_whitespace matching for multi-pattern Host lines.
Sourcepub fn remove_forward(
&mut self,
alias: &str,
directive_key: &str,
value: &str,
) -> bool
pub fn remove_forward( &mut self, alias: &str, directive_key: &str, value: &str, ) -> bool
Remove a specific forwarding directive from a host block. Matches key (case-insensitive) and value (whitespace-normalized). Uses split_whitespace matching for multi-pattern Host lines. Returns true if a directive was actually removed.
Sourcepub fn has_forward(&self, alias: &str, directive_key: &str, value: &str) -> bool
pub fn has_forward(&self, alias: &str, directive_key: &str, value: &str) -> bool
Check if a host block has a specific forwarding directive. Uses whitespace-normalized value comparison and split_whitespace host matching.
Sourcepub fn find_tunnel_directives(&self, alias: &str) -> Vec<TunnelRule>
pub fn find_tunnel_directives(&self, alias: &str) -> Vec<TunnelRule>
Find tunnel directives for a host alias, searching all elements including Include files. Uses split_whitespace matching like has_host() for multi-pattern Host lines.
Sourcepub fn deduplicate_alias(&self, base: &str) -> String
pub fn deduplicate_alias(&self, base: &str) -> String
Generate a unique alias by appending -2, -3, etc. if the base alias is taken.
Sourcepub fn deduplicate_alias_excluding(
&self,
base: &str,
exclude: Option<&str>,
) -> String
pub fn deduplicate_alias_excluding( &self, base: &str, exclude: Option<&str>, ) -> String
Generate a unique alias, optionally excluding one alias from collision detection. Used during rename so the host being renamed doesn’t collide with itself.
Set tags on a host block by alias.
Refuses pattern aliases and multi-alias blocks symmetric with the vault/certificate setters: a tag on a shared block silently applies to every sibling alias, which is rarely the user’s intent.
Set provider-synced tags on a host block by alias.
Same multi-alias and pattern refusal as the other purple-marker setters. Provider tags drive sync decisions, so a wrong-block mutation can cascade into delete/stale.
Sourcepub fn set_host_askpass(&mut self, alias: &str, source: &str) -> bool
pub fn set_host_askpass(&mut self, alias: &str, source: &str) -> bool
Set askpass source on a host block by alias.
Askpass is an authentication credential source; applying it to a sibling alias in a shared block would route the wrong credential.
Sourcepub fn set_host_vault_ssh(&mut self, alias: &str, role: &str) -> bool
pub fn set_host_vault_ssh(&mut self, alias: &str, role: &str) -> bool
Set or remove the Vault SSH role comment on a host block by alias.
Empty role removes the comment.
Mirrors the safety invariants of set_host_certificate_file and
set_host_vault_addr: wildcard aliases are refused so a Host *.prod
pattern can never have a Vault role silently assigned to every host
it resolves, and multi-alias blocks (Host web-01 web-01.prod) are
refused so the role is never applied to sibling aliases the user did
not authorise. Returns true on a successful mutation, false when
the alias is invalid, missing, or lives in an Include file.
Callers that run asynchronously (form submit handlers, sync workers) MUST check the return value so a silent config mutation failure is surfaced instead of pretending the role was wired up.
Sourcepub fn set_host_vault_addr(&mut self, alias: &str, url: &str) -> bool
pub fn set_host_vault_addr(&mut self, alias: &str, url: &str) -> bool
Set or remove the Vault SSH endpoint comment on a host block by alias.
Empty url removes the comment.
Mirrors the safety invariants of set_host_certificate_file: wildcard
aliases are refused to avoid accidentally applying a vault address to
every host resolved through a pattern, and Match blocks are not
touched (they live as inert GlobalLines). Returns true on a
successful mutation, false when the alias is invalid or the block
is not found.
Callers that run asynchronously (e.g. form submit handlers that resolve the alias before writing) MUST check the return value so a silent config mutation failure is surfaced instead of pretending the vault address was wired up.
Sourcepub fn set_host_certificate_file(&mut self, alias: &str, path: &str) -> bool
pub fn set_host_certificate_file(&mut self, alias: &str, path: &str) -> bool
Set or remove the CertificateFile directive on a host block by alias.
Empty path removes the directive.
Set the CertificateFile directive on the host block that matches
alias exactly. Returns true if a matching block was found and
updated, false if no top-level HostBlock matched (alias was
renamed, deleted or lives only inside an Included file).
Only touches CertificateFile directives that are purple-managed
(path contains .purple/certs/). User-set custom CertificateFile
entries (e.g. a corporate or personal cert at ~/.ssh/corp-cert.pub)
are never modified or removed: empty-path clears only the purple
managed line; non-empty path updates the purple-managed line in
place or inserts a new one if absent. A host with both a corporate
cert and a Vault-signed cert ends up with both lines present, in
OpenSSH’s documented cumulative semantics.
Callers that run asynchronously (e.g. the Vault SSH bulk-sign worker) MUST check the return value so a silent config mutation failure is surfaced to the user instead of pretending the cert was wired up.
Sourcepub fn set_host_meta(&mut self, alias: &str, meta: &[(String, String)]) -> bool
pub fn set_host_meta(&mut self, alias: &str, meta: &[(String, String)]) -> bool
Set provider metadata on a host block by alias.
Refuses pattern aliases and multi-alias blocks; same rationale as the
other # purple:* setters.
Sourcepub fn set_host_stale(&mut self, alias: &str, timestamp: u64) -> bool
pub fn set_host_stale(&mut self, alias: &str, timestamp: u64) -> bool
Mark a host as stale by alias.
Stale markers drive the X purge flow which deletes the full block,
so a wrong-block mutation here cascades into data loss for a sibling
alias the user added by hand. Refuse pattern and multi-alias blocks.
Sourcepub fn clear_host_stale(&mut self, alias: &str) -> bool
pub fn clear_host_stale(&mut self, alias: &str) -> bool
Clear stale marking from a host by alias.
Symmetric guard with set_host_stale. Clearing on a shared block is
benign but the asymmetry would be confusing; reject for consistency.
Sourcepub fn stale_hosts(&self) -> Vec<(String, u64)>
pub fn stale_hosts(&self) -> Vec<(String, u64)>
Collect all stale hosts with their timestamps.
Sourcepub fn delete_host(&mut self, alias: &str)
pub fn delete_host(&mut self, alias: &str)
Delete a host entry by alias.
For a single-alias block this removes the whole block (and cleans up
any orphaned # purple:group header left behind). For a multi-alias
block like Host web-01 web-01.prod 10.0.1.5 only the matching
alias token is stripped from the Host line; sibling aliases and
all directives are preserved so that delete_host("web-01.prod")
does not silently wipe configuration for web-01 and 10.0.1.5.
Callers that want to remove the entire block regardless of sibling aliases should surface an explicit confirmation in the UI and then delete each sibling alias in turn.
Sourcepub fn delete_host_undoable(
&mut self,
alias: &str,
) -> Option<(ConfigElement, usize)>
pub fn delete_host_undoable( &mut self, alias: &str, ) -> Option<(ConfigElement, usize)>
Delete a host and return the removed element and its position for undo.
Does NOT collapse blank lines or remove group headers so the position
stays valid for re-insertion via insert_host_at().
Orphaned group headers (if any) are cleaned up at next startup.
For multi-alias blocks this returns None: undoable-delete of a
single alias out of a shared Host line cannot be round-tripped via
insert_host_at because sibling aliases would be lost. Callers
should fall back to delete_host in that case (which strips only
the requested token).
Sourcepub fn insert_host_at(&mut self, element: ConfigElement, position: usize)
pub fn insert_host_at(&mut self, element: ConfigElement, position: usize)
Insert a host block at a specific position (for undo).
Sourcepub fn find_provider_insert_position(
&self,
provider_name: &str,
) -> Option<usize>
pub fn find_provider_insert_position( &self, provider_name: &str, ) -> Option<usize>
Find the position after the last HostBlock that belongs to a provider.
Returns None if no hosts for this provider exist in the config.
Used by the sync engine to insert new hosts adjacent to existing provider hosts.
Sourcepub fn swap_hosts(&mut self, alias_a: &str, alias_b: &str) -> bool
pub fn swap_hosts(&mut self, alias_a: &str, alias_b: &str) -> bool
Swap two host blocks in the config by alias. Returns true if swap was performed.
Source§impl SshConfigFile
impl SshConfigFile
Sourcepub fn parse(path: &Path) -> Result<Self>
pub fn parse(path: &Path) -> Result<Self>
Parse an SSH config file from the given path. Preserves all formatting, comments, and unknown directives for round-trip fidelity.
Sourcepub fn from_content(content: &str, synthetic_path: PathBuf) -> Self
pub fn from_content(content: &str, synthetic_path: PathBuf) -> Self
Create an SshConfigFile from raw content string (for demo/test use). Uses a synthetic path; the file is never read from or written to disk.
Sourcepub fn parse_content(content: &str) -> Vec<ConfigElement>
pub fn parse_content(content: &str) -> Vec<ConfigElement>
Parse SSH config content from a string (without Include resolution). Used by tests to create SshConfigFile from inline strings.
Source§impl SshConfigFile
impl SshConfigFile
Sourcepub fn remove_all_orphaned_group_headers(&mut self) -> usize
pub fn remove_all_orphaned_group_headers(&mut self) -> usize
Remove all # purple:group <DisplayName> GlobalLines that point at a
provider with no remaining Host blocks. Returns the count removed.
Sourcepub fn repair_absorbed_group_comments(&mut self) -> usize
pub fn repair_absorbed_group_comments(&mut self) -> usize
Repair configs where # purple:group comments were absorbed into the
preceding host block’s directives instead of being stored as
GlobalLines. Returns the number of blocks that were repaired.
Only relocates lines whose suffix matches a known provider’s display
name. A user-authored comment like # purple:group canary notes is
left in place: the suffix canary notes is not a provider name, so
the comment is treated as free-form prose rather than a misparsed
group header. This protects hand-written comments from being silently
scrubbed by the next-startup remove_all_orphaned_group_headers pass.
Source§impl SshConfigFile
impl SshConfigFile
Sourcepub fn write(&self) -> Result<()>
pub fn write(&self) -> Result<()>
Write the config back to disk. Creates a backup before writing and uses atomic write (temp file + rename). Resolves symlinks so the rename targets the real file, not the link. Acquires an advisory lock to prevent concurrent writes from multiple purple processes or background sync threads.
Trait Implementations§
Source§impl Clone for SshConfigFile
impl Clone for SshConfigFile
Source§fn clone(&self) -> SshConfigFile
fn clone(&self) -> SshConfigFile
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for SshConfigFile
impl RefUnwindSafe for SshConfigFile
impl Send for SshConfigFile
impl Sync for SshConfigFile
impl Unpin for SshConfigFile
impl UnsafeUnpin for SshConfigFile
impl UnwindSafe for SshConfigFile
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more