package platform import ( "context" "fmt" "io" "os" "os/exec" "path/filepath" "github.com/user/devpack/internal/logging" ) // DarwinPlatform macOS 平台实现 type DarwinPlatform struct { homeDir string } // NewDarwinPlatform 创建 macOS 平台实例 func NewDarwinPlatform() *DarwinPlatform { home, _ := os.UserHomeDir() return &DarwinPlatform{homeDir: home} } func (p *DarwinPlatform) OS() string { return "darwin" } func (p *DarwinPlatform) Arch() string { return "amd64" } // TODO: 正确检测架构 func (p *DarwinPlatform) HomeDir() string { return p.homeDir } func (p *DarwinPlatform) ConfigDir() string { return p.homeDir + "/Library/Application Support" } func (p *DarwinPlatform) DataDir() string { return p.homeDir + "/Library/Application Support" } func (p *DarwinPlatform) GetEnvVar(key string) string { return os.Getenv(key) } func (p *DarwinPlatform) SetEnvVar(ctx context.Context, key, value string) error { // 在 macOS 上通过 launchctl 和 Shell 配置文件设置 cmd := exec.CommandContext(ctx, "launchctl", "setenv", key, value) return cmd.Run() } func (p *DarwinPlatform) AddToPath(ctx context.Context, dir string) error { // TODO: 添加到 Shell 配置文件 return nil } func (p *DarwinPlatform) IsAdmin() bool { return os.Geteuid() == 0 } func (p *DarwinPlatform) PackageManagers() []string { var pms []string if _, err := exec.LookPath("brew"); err == nil { pms = append(pms, "homebrew") } return pms } func (p *DarwinPlatform) DefaultShell() string { shell := os.Getenv("SHELL") if shell != "" { return shell } return "/bin/zsh" } // InstallFont 安装字体到用户级字体目录 // macOS 会自动检测 ~/Library/Fonts/ 中的新字体,无需手动刷新缓存 // 支持 .ttf (TrueType) 和 .otf (OpenType) 格式 func (p *DarwinPlatform) 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) if ext != ".ttf" && ext != ".otf" { log.Errorf("不支持的字体格式: %s", ext) return fmt.Errorf("不支持的字体格式: %s,仅支持 .ttf 和 .otf", ext) } // 用户级字体目录: ~/Library/Fonts/ // macOS 自动监视此目录,放入即可使用 destDir := filepath.Join(p.homeDir, "Library", "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 := darwinCopyFile(fontPath, destPath); err != nil { return fmt.Errorf("复制字体文件失败: %w", err) } // 可选:通过 atsutil 强制刷新字体服务缓存以立即生效 // 某些应用可能需要重启才能看到新字体 if _, err := exec.LookPath("atsutil"); err == nil { // 移除用户字体数据库缓存 cmd := exec.CommandContext(ctx, "atsutil", "databases", "-removeUser") if out, err := cmd.CombinedOutput(); err != nil { log.Debugf("清理字体缓存数据库: %s, %v", string(out), err) } // 重启字体服务 cmd = exec.CommandContext(ctx, "atsutil", "server", "-shutdown") if out, err := cmd.CombinedOutput(); err != nil { log.Debugf("重启字体服务: %s, %v", string(out), err) } } log.Infof("字体安装成功: %s", fontName) return nil } // darwinCopyFile 复制文件,失败时自动清理目标文件 func darwinCopyFile(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 }