package platform import ( "context" "fmt" "io" "os" "os/exec" "path/filepath" "github.com/user/devpack/internal/logging" "golang.org/x/sys/windows/registry" ) // WindowsPlatform Windows 平台实现 type WindowsPlatform struct { homeDir string } // NewWindowsPlatform 创建 Windows 平台实例 func NewWindowsPlatform() *WindowsPlatform { return &WindowsPlatform{ homeDir: os.Getenv("USERPROFILE"), } } func (p *WindowsPlatform) OS() string { return "windows" } func (p *WindowsPlatform) Arch() string { return os.Getenv("PROCESSOR_ARCHITECTURE") } func (p *WindowsPlatform) HomeDir() string { return p.homeDir } func (p *WindowsPlatform) ConfigDir() string { if dir := os.Getenv("APPDATA"); dir != "" { return dir } return p.homeDir + "\\AppData\\Roaming" } func (p *WindowsPlatform) DataDir() string { if dir := os.Getenv("LOCALAPPDATA"); dir != "" { return dir } return p.homeDir + "\\AppData\\Local" } func (p *WindowsPlatform) GetEnvVar(key string) string { return os.Getenv(key) } func (p *WindowsPlatform) SetEnvVar(ctx context.Context, key, value string) error { cmd := exec.CommandContext(ctx, "setx", key, value) return cmd.Run() } func (p *WindowsPlatform) AddToPath(ctx context.Context, dir string) error { currentPath := os.Getenv("PATH") newPath := dir + ";" + currentPath return p.SetEnvVar(ctx, "PATH", newPath) } func (p *WindowsPlatform) IsAdmin() bool { // 检查 Windows 上是否以管理员身份运行 cmd := exec.Command("net", "session") err := cmd.Run() return err == nil } func (p *WindowsPlatform) PackageManagers() []string { var pms []string if _, err := exec.LookPath("scoop"); err == nil { pms = append(pms, "scoop") } if _, err := exec.LookPath("choco"); err == nil { pms = append(pms, "chocolatey") } if _, err := exec.LookPath("winget"); err == nil { pms = append(pms, "winget") } return pms } func (p *WindowsPlatform) DefaultShell() string { if _, err := exec.LookPath("pwsh"); err == nil { return "pwsh" // PowerShell 7+ 版本 } return "powershell" // Windows PowerShell 5.1 } // InstallFont 安装字体到用户级字体目录并注册到注册表 // 支持 .ttf (TrueType) 和 .otf (OpenType) 格式 func (p *WindowsPlatform) InstallFont(ctx context.Context, fontPath string) error { log := logging.G() // 验证源字体文件是否存在且可访问 info, err := os.Stat(fontPath) if err != nil { log.Errorf("无法访问字体文件: %v", err) return fmt.Errorf("无法访问字体文件: %w", err) } if info.IsDir() { return fmt.Errorf("字体路径是目录而非文件: %s", fontPath) } // 检查字体格式是否受支持 fontName := filepath.Base(fontPath) ext := filepath.Ext(fontName) var regSuffix string switch ext { case ".ttf": regSuffix = " (TrueType)" case ".otf": regSuffix = " (OpenType)" default: log.Errorf("不支持的字体格式: %s", ext) return fmt.Errorf("不支持的字体格式: %s,仅支持 .ttf 和 .otf", ext) } // 目标路径为用户级字体目录 // C:\Users\\AppData\Local\Microsoft\Windows\Fonts destDir := filepath.Join(os.Getenv("LOCALAPPDATA"), "Microsoft", "Windows", "Fonts") log.Debugf("正在安装字体: %s 到 %s", fontPath, destDir) // 确保目标目录存在 if err := os.MkdirAll(destDir, 0755); err != nil { return fmt.Errorf("创建字体目录失败: %w", err) } // 检查字体是否已安装 destPath := filepath.Join(destDir, fontName) if _, err := os.Stat(destPath); err == nil { log.Infof("字体已存在,跳过安装: %s", destPath) return nil } // 复制字体文件到目标路径 if err := copyFile(fontPath, destPath); err != nil { return fmt.Errorf("复制字体文件失败: %w", err) } // 写注册表以注册字体(用户级,无需管理员权限) key, _, err := registry.CreateKey( registry.CURRENT_USER, `Software\Microsoft\Windows NT\CurrentVersion\Fonts`, registry.ALL_ACCESS, ) if err != nil { // 注册表写入失败时清理已复制的字体文件 os.Remove(destPath) return fmt.Errorf("打开字体注册表失败: %w", err) } defer key.Close() // 注册表键名:去掉扩展名的字体名 + 类型后缀 baseName := fontName[:len(fontName)-len(ext)] regName := baseName + regSuffix if err := key.SetStringValue(regName, destPath); err != nil { os.Remove(destPath) return fmt.Errorf("注册字体失败: %w", err) } log.Infof("字体安装成功: %s", fontName) return nil } // copyFile 复制文件,失败时自动清理目标文件 func copyFile(src, dst string) error { input, err := os.Open(src) if err != nil { return err } defer input.Close() output, err := os.Create(dst) if err != nil { return err } _, err = io.Copy(output, input) // 先关闭文件确保数据写入磁盘 closeErr := output.Close() if err != nil { os.Remove(dst) // 复制失败,清理残留文件 return err } if closeErr != nil { os.Remove(dst) return closeErr } return nil }