// Copyright 2025. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package file import ( "bufio" "io" "os" "path/filepath" "strconv" "strings" "github.com/Adembc/lazyssh/internal/core/domain" ) const ( sshConfigAliasField = "host" sshConfigIPField = "hostname" sshConfigUserField = "user" sshConfigPortField = "port" sshConfigKeyField = "identityfile" ) type SSHConfigParser struct{} func (p *SSHConfigParser) Parse(reader io.Reader) ([]domain.Server, error) { servers := make([]domain.Server, 0) var currentServer *domain.Server scanner := bufio.NewScanner(reader) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if p.shouldSkipLine(line) { continue } key, value := p.parseKeyValue(line) if key == "" { continue } switch key { case sshConfigAliasField: if currentServer != nil { servers = append(servers, *currentServer) } currentServer = &domain.Server{ Alias: value, Port: DefaultPort, } case sshConfigIPField: if currentServer != nil { currentServer.Host = value } case sshConfigUserField: if currentServer != nil { currentServer.User = value } case sshConfigPortField: if currentServer != nil { currentServer.Port = p.parsePort(value) } case sshConfigKeyField: if currentServer != nil { currentServer.Key = p.expandPath(value) } } } if currentServer != nil { servers = append(servers, *currentServer) } return servers, scanner.Err() } func (p *SSHConfigParser) shouldSkipLine(line string) bool { return line == "" || strings.HasPrefix(line, "#") } func (p *SSHConfigParser) parseKeyValue(line string) (string, string) { parts := strings.Fields(line) if len(parts) < 2 { return "", "" } key := strings.ToLower(parts[0]) value := strings.Join(parts[1:], " ") return key, value } func (p *SSHConfigParser) parsePort(value string) int { if port, err := strconv.Atoi(value); err == nil { return port } return DefaultPort } func (p *SSHConfigParser) expandPath(path string) string { if strings.HasPrefix(path, "~/") { if home, err := os.UserHomeDir(); err == nil { return filepath.Join(home, path[2:]) } } return path }