Skip to content

Commit

Permalink
Merge pull request #6 from mrf345/testing
Browse files Browse the repository at this point in the history
Add mime type support, and remove version from file names
  • Loading branch information
mrf345 authored Sep 10, 2024
2 parents dcf394d + c63481e commit e326023
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 42 deletions.
145 changes: 104 additions & 41 deletions desktopEntry.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
//
// With the default settings shown in [desktopEntry.DesktopEntry] the method [desktopEntry.DesktopEntry.Create]
// will check your [desktopEntry.DesktopEntry.AppsPath] for a .desktop file, that matches your
// [desktopEntry.DesktopEntry.Name]-[desktopEntry.DesktopEntry.Version], if it can't find it, it'll create a new one.
// [desktopEntry.DesktopEntry.Name], if it can't find it, it'll create a new one.
// That will later on be updated it only when the binary path changes.
// See test [example].
//
// [example]: https://pkg.go.dev/github.com/mrf345/desktop-entry#example-DesktopEntry.Create
package desktopEntry

import (
"errors"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -57,6 +58,16 @@ type DesktopEntry struct {
UpdateIfChanged bool
// Rerun the app if desktop entry has changed (default: true)
RerunIfChanged bool
// Mime type to associate the desktop entry with
MimeType MimeType
}

type MimeType struct {
Type string
Path string
Comment string
GenericIcon string
Patterns []string
}

// Create a new [desktopEntry.DesktopEntry] instance with the default options
Expand All @@ -67,7 +78,7 @@ func New(name, version string, icon []byte) *DesktopEntry {
Icon: icon,
Type: "Application",
Arch: "x86_64",
Perm: 0776,
Perm: 0755,
AppsPath: fmt.Sprintf("%s/.local/share/applications", os.Getenv("HOME")),
IconsPath: fmt.Sprintf("%s/.icons", os.Getenv("HOME")),
OSs: []string{"linux"},
Expand All @@ -82,8 +93,9 @@ func (de *DesktopEntry) Create() (err error) {

isDevBuild := strings.HasPrefix(os.Args[0], os.TempDir())
isSupportedOs := slices.Contains(de.OSs, runtime.GOOS)
shouldCreateMimeType, err := de.shouldCreateMimeType()

if isDevBuild || !isSupportedOs {
if isDevBuild || !isSupportedOs || err != nil {
return
}

Expand All @@ -102,15 +114,28 @@ func (de *DesktopEntry) Create() (err error) {
return
}

if shouldCreateMimeType {
if err = de.createMimeType(); err != nil {
err = fmt.Errorf("failed to create mime type file > %w", err)
return
}
}

if changed && de.RerunIfChanged {
err = de.restart()
err = restart()
}

return
}

func (de *DesktopEntry) createPaths() (err error) {
for _, path := range []string{de.AppsPath, de.IconsPath} {
func (de DesktopEntry) createPaths() (err error) {
paths := []string{de.AppsPath, de.IconsPath}

if len(de.MimeType.Path) > 0 {
paths = append(paths, de.MimeType.Path)
}

for _, path := range paths {
if _, err = os.Stat(path); os.IsNotExist(err) {
if err = os.MkdirAll(path, de.Perm); err != nil {
return
Expand All @@ -124,7 +149,7 @@ func (de *DesktopEntry) createPaths() (err error) {
return
}

func (de *DesktopEntry) createIcon() (err error) {
func (de DesktopEntry) createIcon() (err error) {
var iconPath = de.getIconPath()

if _, err = os.Stat(iconPath); !os.IsNotExist(err) {
Expand All @@ -134,15 +159,15 @@ func (de *DesktopEntry) createIcon() (err error) {
return os.WriteFile(iconPath, de.Icon, de.Perm)
}

func (de *DesktopEntry) getIconPath() string {
func (de DesktopEntry) getIconPath() string {
return filepath.Join(de.IconsPath, de.getID()+".png")
}

func (de *DesktopEntry) getID() string {
return fmt.Sprintf("%s-%s", de.Name, de.Version)
func (de DesktopEntry) getID() string {
return strings.ToLower(de.Name)
}

func (de *DesktopEntry) createEntry() (changed bool, err error) {
func (de DesktopEntry) createEntry() (changed bool, err error) {
var entryPath = filepath.Join(de.AppsPath, de.getID()+".desktop")
var entryData string
var doUpdate = de.UpdateIfChanged
Expand All @@ -164,13 +189,16 @@ func (de *DesktopEntry) createEntry() (changed bool, err error) {
return
}

return true, os.WriteFile(entryPath, []byte(entryData), de.Perm)
changed = true
err = os.WriteFile(entryPath, []byte(entryData), de.Perm)
_ = exec.Command(fmt.Sprintf(`update-desktop-database "%s"`, de.AppsPath)).Run()
return
}

return
}

func (de *DesktopEntry) shouldUpdate(entryPath string) (yes bool, err error) {
func (de DesktopEntry) shouldUpdate(entryPath string) (yes bool, err error) {
var entryFile *os.File
var execRegex, classRegex *regexp.Regexp
var existingData []byte
Expand All @@ -193,37 +221,25 @@ func (de *DesktopEntry) shouldUpdate(entryPath string) (yes bool, err error) {
return
}

if execLine, err = de.getExecLine(); err != nil {
if execLine, err = getExecLine(); err != nil {
return
}

if match := execRegex.Find(existingData); match == nil || string(match) != execLine {
yes = true
}

if match := classRegex.Find(existingData); match == nil || string(match) != de.getStartupClassLine() {
if match := classRegex.Find(existingData); match == nil || string(match) != getStartupClassLine() {
yes = true
}

return
}

func (de *DesktopEntry) getExecLine() (execPath string, err error) {
if execPath, err = os.Executable(); err != nil {
return
}

return fmt.Sprintf("Exec=sh -c '%s %%F'", execPath), nil
}

func (de *DesktopEntry) getStartupClassLine() string {
return "StartupWMClass=" + filepath.Base(os.Args[0])
}

func (de *DesktopEntry) getEntryContent() (content string, err error) {
func (de DesktopEntry) getEntryContent() (content string, err error) {
var execLine string

if execLine, err = de.getExecLine(); err != nil {
if execLine, err = getExecLine(); err != nil {
return
}

Expand All @@ -233,7 +249,7 @@ func (de *DesktopEntry) getEntryContent() (content string, err error) {
"Name=" + de.Name,
execLine,
"Icon=" + de.getIconPath(),
de.getStartupClassLine(),
getStartupClassLine(),
}

if de.Categories != "" {
Expand All @@ -244,26 +260,73 @@ func (de *DesktopEntry) getEntryContent() (content string, err error) {
lines = append(lines, "Comment="+de.Comment)
}

if len(de.MimeType.Type) > 0 {
lines = append(lines, "MimeType="+de.MimeType.Type)
}

return strings.Join(lines, "\n"), nil
}

func (de *DesktopEntry) restart() (err error) {
var cmd *exec.Cmd
func (de DesktopEntry) shouldCreateMimeType() (yes bool, err error) {
var path string

if len(os.Args) > 1 {
cmd = exec.Command(os.Args[0], os.Args[1:]...)
} else {
cmd = exec.Command(os.Args[0])
if len(de.MimeType.Type) == 0 || len(de.MimeType.Path) == 0 {
return
}

cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if path, err = de.getMimeTypePath(); err != nil {
return
}

if err = cmd.Run(); err != nil {
if _, err = os.Stat(path); err != nil && !os.IsNotExist(err) {
return
}

os.Exit(0)
return true, nil
}

func (de DesktopEntry) getMimeTypePath() (string, error) {
typeChunks := strings.Split(de.MimeType.Type, "/")

if 1 >= len(typeChunks) {
return "", errors.New("invalid MimeType.Type")
}

return filepath.Join(de.MimeType.Path, "packages/"+typeChunks[1]+".xml"), nil
}

func (de DesktopEntry) createMimeType() (err error) {
var path string

lines := []string{
`<?xml version="1.0" encoding="utf-8"?>`,
`<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">`,
fmt.Sprintf(` <mime-type type="%s">`, de.MimeType.Type),
}

for _, p := range de.MimeType.Patterns {
lines = append(lines, fmt.Sprintf(` <glob pattern="%s"/>`, p))
}

if len(de.MimeType.Comment) > 0 {
lines = append(lines, fmt.Sprintf(` <comment>%s</comment>`, de.MimeType.Comment))
}

if len(de.MimeType.GenericIcon) > 0 {
lines = append(lines, fmt.Sprintf(` <generic-icon name="%s"/>`, de.MimeType.GenericIcon))
}

lines = append(lines, []string{" </mime-type>", "</mime-info>"}...)

if path, err = de.getMimeTypePath(); err != nil {
return
}

if err = os.WriteFile(path, []byte(strings.Join(lines, "\n")), de.Perm); err != nil {
return
}

_ = exec.Command(fmt.Sprintf(`update-mime-database "%s"`, de.MimeType.Path)).Run()

return
}
2 changes: 1 addition & 1 deletion desktopEntry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func getTestEntry() (

name := "testing"
version := "0.0.1"
id := fmt.Sprintf("%s-%s", name, version)
id := name
entry = desktopEntry.New(
name,
version,
Expand Down
41 changes: 41 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package desktopEntry

import (
"fmt"
"os"
"os/exec"
"path/filepath"
)

func getExecLine() (execPath string, err error) {
if execPath, err = os.Executable(); err != nil {
return
}

return fmt.Sprintf("Exec=sh -c '%s %%F'", execPath), nil
}

func getStartupClassLine() string {
return "StartupWMClass=" + filepath.Base(os.Args[0])
}

func restart() (err error) {
var cmd *exec.Cmd

if len(os.Args) > 1 {
cmd = exec.Command(os.Args[0], os.Args[1:]...)
} else {
cmd = exec.Command(os.Args[0])
}

cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err = cmd.Run(); err != nil {
return
}

os.Exit(0)
return
}

0 comments on commit e326023

Please sign in to comment.