using DodoSSH.Client.Domain;
namespace DodoSSH.Client.Import;
///
/// One host an ssh_config describes, resolved and ready to be looked at.
///
///
/// Deliberately not a . This is a candidate somebody has not agreed to import yet,
/// and it carries things a stored host has no field for — the identity file's path, the jump alias by name,
/// and the warnings that go beside a row in the preview.
///
///
/// The name from the Host line, which is what the user types after ssh and so the name they
/// will recognise.
///
///
/// What HostName said, or the alias when it said nothing — which is OpenSSH's own default and the
/// reason Host db.internal with no other directive works.
///
/// What User said, if anything.
/// What Port said, defaulting to 22.
/// Every IdentityFile path, in the order they were given.
/// The ProxyJump value verbatim, if any.
/// Everything else, as SSH directives.
/// What could not be represented, per host.
public sealed record ImportedHost(
string Alias,
string Hostname,
string? Username,
int Port,
IReadOnlyList IdentityFiles,
string? ProxyJump,
HostOptions Options,
IReadOnlyList Warnings)
{
/// The address this would dial, for a preview row.
public string Address => Username is { Length: > 0 } user
? $"{user}@{Hostname}:{Port}"
: $"{Hostname}:{Port}";
/// Turns this into the host that would be stored.
///
///
/// The identity file becomes a note and a directive, not a key. Reading somebody's
/// ~/.ssh/id_ed25519 into a keychain is exactly the act this product exists to make deliberate,
/// and doing it as a side effect of "import my config" is the wrong default. The path is recorded so it
/// is not lost; importing the material is a separate, per-row choice.
///
///
/// ProxyJump records intent and changes nothing about connecting. The SSH layer has no jump
/// hosts — ISshConnection offers OpenShellAsync and nothing else, and
/// SshConnectionRequest has no route field. So it is kept as a directive and a note, and the
/// preview says so; a bastion topology that imported and quietly did not route would be worse than one
/// that was not imported.
///
///
public HostSecret ToSecret()
{
var options = new List(Options);
var notes = new List();
if (IdentityFiles.Count > 0)
{
options.Add(new HostOption("IdentityFile", IdentityFiles[0]));
notes.Add(IdentityFiles.Count == 1
? $"ssh_config used the key at {IdentityFiles[0]}."
: $"ssh_config listed {IdentityFiles.Count} keys, the first being {IdentityFiles[0]}.");
}
if (ProxyJump is { Length: > 0 } jump)
{
options.Add(new HostOption("ProxyJump", jump));
notes.Add($"ssh_config reached this through {jump}. DodoSSH does not route through a jump host yet.");
}
return new HostSecret
{
Label = Alias,
Hostname = Hostname,
Port = Port,
Username = Username,
Notes = notes.Count == 0 ? null : string.Join(" ", notes),
Options = HostOptions.Create(options),
};
}
}
///
/// Everything an ssh_config yielded: the hosts it can offer, and what it could not.
///
/// The importable candidates, in file order.
///
/// Host patterns that are patterns rather than names. They contribute defaults and are not
/// importable: a bookmark called *.internal is one nothing can dial.
///
/// Document-level notes, including the parser's own.
public sealed record SshConfigImport(
IReadOnlyList Hosts,
IReadOnlyList SkippedPatterns,
IReadOnlyList Warnings);