1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

Compare commits

...

3 Commits

Author SHA1 Message Date
bbedward
e6c3ae9397 cups: add comprehensive CUPs setting page
- Add printers
- Delete printers
- Use polkit APIs as fallback on auth errors
- Fix ref system to conditionally subscribe to cups when wanted
2025-11-29 17:35:21 -05:00
bbedward
df663aceb9 net: less Theme.success 2025-11-29 11:14:15 -05:00
bbedward
db7e597f67 DankDash: fix per-monitor wallpapers 2025-11-29 11:10:10 -05:00
34 changed files with 6004 additions and 562 deletions

View File

@@ -34,12 +34,16 @@ packages:
outpkg: mocks_network
interfaces:
Backend:
github.com/AvengeMedia/danklinux/internal/server/cups:
github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups:
config:
dir: "internal/mocks/cups"
outpkg: mocks_cups
interfaces:
CUPSClientInterface:
PkHelper:
config:
dir: "internal/mocks/cups_pkhelper"
outpkg: mocks_cups_pkhelper
github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev:
config:
dir: "internal/mocks/evdev"

View File

@@ -140,6 +140,7 @@ func runVersion(cmd *cobra.Command, args []string) {
}
func startDebugServer() error {
server.CLIVersion = Version
return server.Start(true)
}

View File

@@ -298,7 +298,8 @@ func ensureGreetdEnabled() error {
fmt.Println(" ✓ Unmasked greetd")
}
if state.EnabledState == "disabled" || state.EnabledState == "masked" || state.EnabledState == "masked-runtime" {
switch state.EnabledState {
case "disabled", "masked", "masked-runtime":
fmt.Println(" Enabling greetd service...")
enableCmd := exec.Command("sudo", "systemctl", "enable", "greetd")
enableCmd.Stdout = os.Stdout
@@ -307,9 +308,9 @@ func ensureGreetdEnabled() error {
return fmt.Errorf("failed to enable greetd: %w", err)
}
fmt.Println(" ✓ Enabled greetd service")
} else if state.EnabledState == "enabled" || state.EnabledState == "enabled-runtime" {
case "enabled", "enabled-runtime":
fmt.Println(" ✓ greetd is already enabled")
} else {
default:
fmt.Printf(" greetd is in state '%s' (should work, no action needed)\n", state.EnabledState)
}

View File

@@ -383,6 +383,7 @@ func runShellDaemon(session bool) {
errChan <- fmt.Errorf("server panic: %v", r)
}
}()
server.CLIVersion = Version
if err := server.Start(false); err != nil {
errChan <- fmt.Errorf("server error: %w", err)
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
package config
import _ "embed"
//go:embed embedded/testpage.pdf
var TestPage string

View File

@@ -22,6 +22,99 @@ func (_m *MockCUPSClientInterface) EXPECT() *MockCUPSClientInterface_Expecter {
return &MockCUPSClientInterface_Expecter{mock: &_m.Mock}
}
// AcceptJobs provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) AcceptJobs(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for AcceptJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_AcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AcceptJobs'
type MockCUPSClientInterface_AcceptJobs_Call struct {
*mock.Call
}
// AcceptJobs is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) AcceptJobs(printer interface{}) *MockCUPSClientInterface_AcceptJobs_Call {
return &MockCUPSClientInterface_AcceptJobs_Call{Call: _e.mock.On("AcceptJobs", printer)}
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) Return(_a0 error) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Return(run)
return _c
}
// AddPrinterToClass provides a mock function with given fields: class, printer
func (_m *MockCUPSClientInterface) AddPrinterToClass(class string, printer string) error {
ret := _m.Called(class, printer)
if len(ret) == 0 {
panic("no return value specified for AddPrinterToClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(class, printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_AddPrinterToClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddPrinterToClass'
type MockCUPSClientInterface_AddPrinterToClass_Call struct {
*mock.Call
}
// AddPrinterToClass is a helper method to define mock.On call
// - class string
// - printer string
func (_e *MockCUPSClientInterface_Expecter) AddPrinterToClass(class interface{}, printer interface{}) *MockCUPSClientInterface_AddPrinterToClass_Call {
return &MockCUPSClientInterface_AddPrinterToClass_Call{Call: _e.mock.On("AddPrinterToClass", class, printer)}
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Return(_a0 error) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Return(run)
return _c
}
// CancelAllJob provides a mock function with given fields: printer, purge
func (_m *MockCUPSClientInterface) CancelAllJob(printer string, purge bool) error {
ret := _m.Called(printer, purge)
@@ -116,6 +209,312 @@ func (_c *MockCUPSClientInterface_CancelJob_Call) RunAndReturn(run func(int, boo
return _c
}
// CreatePrinter provides a mock function with given fields: name, deviceURI, ppd, shared, errorPolicy, information, location
func (_m *MockCUPSClientInterface) CreatePrinter(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string) error {
ret := _m.Called(name, deviceURI, ppd, shared, errorPolicy, information, location)
if len(ret) == 0 {
panic("no return value specified for CreatePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, bool, string, string, string) error); ok {
r0 = rf(name, deviceURI, ppd, shared, errorPolicy, information, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_CreatePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrinter'
type MockCUPSClientInterface_CreatePrinter_Call struct {
*mock.Call
}
// CreatePrinter is a helper method to define mock.On call
// - name string
// - deviceURI string
// - ppd string
// - shared bool
// - errorPolicy string
// - information string
// - location string
func (_e *MockCUPSClientInterface_Expecter) CreatePrinter(name interface{}, deviceURI interface{}, ppd interface{}, shared interface{}, errorPolicy interface{}, information interface{}, location interface{}) *MockCUPSClientInterface_CreatePrinter_Call {
return &MockCUPSClientInterface_CreatePrinter_Call{Call: _e.mock.On("CreatePrinter", name, deviceURI, ppd, shared, errorPolicy, information, location)}
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) Run(run func(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string)) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool), args[4].(string), args[5].(string), args[6].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) RunAndReturn(run func(string, string, string, bool, string, string, string) error) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Return(run)
return _c
}
// DeleteClass provides a mock function with given fields: class
func (_m *MockCUPSClientInterface) DeleteClass(class string) error {
ret := _m.Called(class)
if len(ret) == 0 {
panic("no return value specified for DeleteClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(class)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeleteClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteClass'
type MockCUPSClientInterface_DeleteClass_Call struct {
*mock.Call
}
// DeleteClass is a helper method to define mock.On call
// - class string
func (_e *MockCUPSClientInterface_Expecter) DeleteClass(class interface{}) *MockCUPSClientInterface_DeleteClass_Call {
return &MockCUPSClientInterface_DeleteClass_Call{Call: _e.mock.On("DeleteClass", class)}
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) Run(run func(class string)) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Return(run)
return _c
}
// DeletePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) DeletePrinter(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for DeletePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinter'
type MockCUPSClientInterface_DeletePrinter_Call struct {
*mock.Call
}
// DeletePrinter is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) DeletePrinter(printer interface{}) *MockCUPSClientInterface_DeletePrinter_Call {
return &MockCUPSClientInterface_DeletePrinter_Call{Call: _e.mock.On("DeletePrinter", printer)}
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Return(run)
return _c
}
// DeletePrinterFromClass provides a mock function with given fields: class, printer
func (_m *MockCUPSClientInterface) DeletePrinterFromClass(class string, printer string) error {
ret := _m.Called(class, printer)
if len(ret) == 0 {
panic("no return value specified for DeletePrinterFromClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(class, printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeletePrinterFromClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinterFromClass'
type MockCUPSClientInterface_DeletePrinterFromClass_Call struct {
*mock.Call
}
// DeletePrinterFromClass is a helper method to define mock.On call
// - class string
// - printer string
func (_e *MockCUPSClientInterface_Expecter) DeletePrinterFromClass(class interface{}, printer interface{}) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
return &MockCUPSClientInterface_DeletePrinterFromClass_Call{Call: _e.mock.On("DeletePrinterFromClass", class, printer)}
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Return(run)
return _c
}
// GetClasses provides a mock function with given fields: attributes
func (_m *MockCUPSClientInterface) GetClasses(attributes []string) (map[string]ipp.Attributes, error) {
ret := _m.Called(attributes)
if len(ret) == 0 {
panic("no return value specified for GetClasses")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func([]string) (map[string]ipp.Attributes, error)); ok {
return rf(attributes)
}
if rf, ok := ret.Get(0).(func([]string) map[string]ipp.Attributes); ok {
r0 = rf(attributes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func([]string) error); ok {
r1 = rf(attributes)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetClasses_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClasses'
type MockCUPSClientInterface_GetClasses_Call struct {
*mock.Call
}
// GetClasses is a helper method to define mock.On call
// - attributes []string
func (_e *MockCUPSClientInterface_Expecter) GetClasses(attributes interface{}) *MockCUPSClientInterface_GetClasses_Call {
return &MockCUPSClientInterface_GetClasses_Call{Call: _e.mock.On("GetClasses", attributes)}
}
func (_c *MockCUPSClientInterface_GetClasses_Call) Run(run func(attributes []string)) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].([]string))
})
return _c
}
func (_c *MockCUPSClientInterface_GetClasses_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetClasses_Call) RunAndReturn(run func([]string) (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Return(run)
return _c
}
// GetDevices provides a mock function with no fields
func (_m *MockCUPSClientInterface) GetDevices() (map[string]ipp.Attributes, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetDevices")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDevices'
type MockCUPSClientInterface_GetDevices_Call struct {
*mock.Call
}
// GetDevices is a helper method to define mock.On call
func (_e *MockCUPSClientInterface_Expecter) GetDevices() *MockCUPSClientInterface_GetDevices_Call {
return &MockCUPSClientInterface_GetDevices_Call{Call: _e.mock.On("GetDevices")}
}
func (_c *MockCUPSClientInterface_GetDevices_Call) Run(run func()) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockCUPSClientInterface_GetDevices_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetDevices_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Return(run)
return _c
}
// GetJobs provides a mock function with given fields: printer, class, whichJobs, myJobs, firstJobId, limit, attributes
func (_m *MockCUPSClientInterface) GetJobs(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string) (map[int]ipp.Attributes, error) {
ret := _m.Called(printer, class, whichJobs, myJobs, firstJobId, limit, attributes)
@@ -180,6 +579,63 @@ func (_c *MockCUPSClientInterface_GetJobs_Call) RunAndReturn(run func(string, st
return _c
}
// GetPPDs provides a mock function with no fields
func (_m *MockCUPSClientInterface) GetPPDs() (map[string]ipp.Attributes, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetPPDs")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetPPDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPPDs'
type MockCUPSClientInterface_GetPPDs_Call struct {
*mock.Call
}
// GetPPDs is a helper method to define mock.On call
func (_e *MockCUPSClientInterface_Expecter) GetPPDs() *MockCUPSClientInterface_GetPPDs_Call {
return &MockCUPSClientInterface_GetPPDs_Call{Call: _e.mock.On("GetPPDs")}
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) Run(run func()) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Return(run)
return _c
}
// GetPrinters provides a mock function with given fields: attributes
func (_m *MockCUPSClientInterface) GetPrinters(attributes []string) (map[string]ipp.Attributes, error) {
ret := _m.Called(attributes)
@@ -238,6 +694,100 @@ func (_c *MockCUPSClientInterface_GetPrinters_Call) RunAndReturn(run func([]stri
return _c
}
// HoldJobUntil provides a mock function with given fields: jobID, holdUntil
func (_m *MockCUPSClientInterface) HoldJobUntil(jobID int, holdUntil string) error {
ret := _m.Called(jobID, holdUntil)
if len(ret) == 0 {
panic("no return value specified for HoldJobUntil")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, holdUntil)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_HoldJobUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HoldJobUntil'
type MockCUPSClientInterface_HoldJobUntil_Call struct {
*mock.Call
}
// HoldJobUntil is a helper method to define mock.On call
// - jobID int
// - holdUntil string
func (_e *MockCUPSClientInterface_Expecter) HoldJobUntil(jobID interface{}, holdUntil interface{}) *MockCUPSClientInterface_HoldJobUntil_Call {
return &MockCUPSClientInterface_HoldJobUntil_Call{Call: _e.mock.On("HoldJobUntil", jobID, holdUntil)}
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Run(run func(jobID int, holdUntil string)) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Return(_a0 error) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Return(run)
return _c
}
// MoveJob provides a mock function with given fields: jobID, destPrinter
func (_m *MockCUPSClientInterface) MoveJob(jobID int, destPrinter string) error {
ret := _m.Called(jobID, destPrinter)
if len(ret) == 0 {
panic("no return value specified for MoveJob")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, destPrinter)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_MoveJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MoveJob'
type MockCUPSClientInterface_MoveJob_Call struct {
*mock.Call
}
// MoveJob is a helper method to define mock.On call
// - jobID int
// - destPrinter string
func (_e *MockCUPSClientInterface_Expecter) MoveJob(jobID interface{}, destPrinter interface{}) *MockCUPSClientInterface_MoveJob_Call {
return &MockCUPSClientInterface_MoveJob_Call{Call: _e.mock.On("MoveJob", jobID, destPrinter)}
}
func (_c *MockCUPSClientInterface_MoveJob_Call) Run(run func(jobID int, destPrinter string)) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_MoveJob_Call) Return(_a0 error) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_MoveJob_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Return(run)
return _c
}
// PausePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) PausePrinter(printer string) error {
ret := _m.Called(printer)
@@ -284,6 +834,156 @@ func (_c *MockCUPSClientInterface_PausePrinter_Call) RunAndReturn(run func(strin
return _c
}
// PrintTestPage provides a mock function with given fields: printer, testPageData, size
func (_m *MockCUPSClientInterface) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) {
ret := _m.Called(printer, testPageData, size)
if len(ret) == 0 {
panic("no return value specified for PrintTestPage")
}
var r0 int
var r1 error
if rf, ok := ret.Get(0).(func(string, io.Reader, int) (int, error)); ok {
return rf(printer, testPageData, size)
}
if rf, ok := ret.Get(0).(func(string, io.Reader, int) int); ok {
r0 = rf(printer, testPageData, size)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func(string, io.Reader, int) error); ok {
r1 = rf(printer, testPageData, size)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_PrintTestPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrintTestPage'
type MockCUPSClientInterface_PrintTestPage_Call struct {
*mock.Call
}
// PrintTestPage is a helper method to define mock.On call
// - printer string
// - testPageData io.Reader
// - size int
func (_e *MockCUPSClientInterface_Expecter) PrintTestPage(printer interface{}, testPageData interface{}, size interface{}) *MockCUPSClientInterface_PrintTestPage_Call {
return &MockCUPSClientInterface_PrintTestPage_Call{Call: _e.mock.On("PrintTestPage", printer, testPageData, size)}
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) Run(run func(printer string, testPageData io.Reader, size int)) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(io.Reader), args[2].(int))
})
return _c
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) Return(_a0 int, _a1 error) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) RunAndReturn(run func(string, io.Reader, int) (int, error)) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Return(run)
return _c
}
// RejectJobs provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) RejectJobs(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for RejectJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_RejectJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RejectJobs'
type MockCUPSClientInterface_RejectJobs_Call struct {
*mock.Call
}
// RejectJobs is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) RejectJobs(printer interface{}) *MockCUPSClientInterface_RejectJobs_Call {
return &MockCUPSClientInterface_RejectJobs_Call{Call: _e.mock.On("RejectJobs", printer)}
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) Return(_a0 error) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Return(run)
return _c
}
// RestartJob provides a mock function with given fields: jobID
func (_m *MockCUPSClientInterface) RestartJob(jobID int) error {
ret := _m.Called(jobID)
if len(ret) == 0 {
panic("no return value specified for RestartJob")
}
var r0 error
if rf, ok := ret.Get(0).(func(int) error); ok {
r0 = rf(jobID)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_RestartJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RestartJob'
type MockCUPSClientInterface_RestartJob_Call struct {
*mock.Call
}
// RestartJob is a helper method to define mock.On call
// - jobID int
func (_e *MockCUPSClientInterface_Expecter) RestartJob(jobID interface{}) *MockCUPSClientInterface_RestartJob_Call {
return &MockCUPSClientInterface_RestartJob_Call{Call: _e.mock.On("RestartJob", jobID)}
}
func (_c *MockCUPSClientInterface_RestartJob_Call) Run(run func(jobID int)) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int))
})
return _c
}
func (_c *MockCUPSClientInterface_RestartJob_Call) Return(_a0 error) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_RestartJob_Call) RunAndReturn(run func(int) error) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Return(run)
return _c
}
// ResumePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) ResumePrinter(printer string) error {
ret := _m.Called(printer)
@@ -390,6 +1090,147 @@ func (_c *MockCUPSClientInterface_SendRequest_Call) RunAndReturn(run func(string
return _c
}
// SetPrinterInformation provides a mock function with given fields: printer, information
func (_m *MockCUPSClientInterface) SetPrinterInformation(printer string, information string) error {
ret := _m.Called(printer, information)
if len(ret) == 0 {
panic("no return value specified for SetPrinterInformation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(printer, information)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterInformation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterInformation'
type MockCUPSClientInterface_SetPrinterInformation_Call struct {
*mock.Call
}
// SetPrinterInformation is a helper method to define mock.On call
// - printer string
// - information string
func (_e *MockCUPSClientInterface_Expecter) SetPrinterInformation(printer interface{}, information interface{}) *MockCUPSClientInterface_SetPrinterInformation_Call {
return &MockCUPSClientInterface_SetPrinterInformation_Call{Call: _e.mock.On("SetPrinterInformation", printer, information)}
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Run(run func(printer string, information string)) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Return(run)
return _c
}
// SetPrinterIsShared provides a mock function with given fields: printer, shared
func (_m *MockCUPSClientInterface) SetPrinterIsShared(printer string, shared bool) error {
ret := _m.Called(printer, shared)
if len(ret) == 0 {
panic("no return value specified for SetPrinterIsShared")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(printer, shared)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterIsShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterIsShared'
type MockCUPSClientInterface_SetPrinterIsShared_Call struct {
*mock.Call
}
// SetPrinterIsShared is a helper method to define mock.On call
// - printer string
// - shared bool
func (_e *MockCUPSClientInterface_Expecter) SetPrinterIsShared(printer interface{}, shared interface{}) *MockCUPSClientInterface_SetPrinterIsShared_Call {
return &MockCUPSClientInterface_SetPrinterIsShared_Call{Call: _e.mock.On("SetPrinterIsShared", printer, shared)}
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Run(run func(printer string, shared bool)) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) RunAndReturn(run func(string, bool) error) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Return(run)
return _c
}
// SetPrinterLocation provides a mock function with given fields: printer, location
func (_m *MockCUPSClientInterface) SetPrinterLocation(printer string, location string) error {
ret := _m.Called(printer, location)
if len(ret) == 0 {
panic("no return value specified for SetPrinterLocation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(printer, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterLocation'
type MockCUPSClientInterface_SetPrinterLocation_Call struct {
*mock.Call
}
// SetPrinterLocation is a helper method to define mock.On call
// - printer string
// - location string
func (_e *MockCUPSClientInterface_Expecter) SetPrinterLocation(printer interface{}, location interface{}) *MockCUPSClientInterface_SetPrinterLocation_Call {
return &MockCUPSClientInterface_SetPrinterLocation_Call{Call: _e.mock.On("SetPrinterLocation", printer, location)}
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Run(run func(printer string, location string)) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Return(run)
return _c
}
// NewMockCUPSClientInterface creates a new instance of MockCUPSClientInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockCUPSClientInterface(t interface {

View File

@@ -0,0 +1,708 @@
// Code generated by mockery v2.53.5. DO NOT EDIT.
package mocks_cups_pkhelper
import (
cups "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups"
mock "github.com/stretchr/testify/mock"
)
// MockPkHelper is an autogenerated mock type for the PkHelper type
type MockPkHelper struct {
mock.Mock
}
type MockPkHelper_Expecter struct {
mock *mock.Mock
}
func (_m *MockPkHelper) EXPECT() *MockPkHelper_Expecter {
return &MockPkHelper_Expecter{mock: &_m.Mock}
}
// ClassAddPrinter provides a mock function with given fields: className, printerName
func (_m *MockPkHelper) ClassAddPrinter(className string, printerName string) error {
ret := _m.Called(className, printerName)
if len(ret) == 0 {
panic("no return value specified for ClassAddPrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(className, printerName)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassAddPrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassAddPrinter'
type MockPkHelper_ClassAddPrinter_Call struct {
*mock.Call
}
// ClassAddPrinter is a helper method to define mock.On call
// - className string
// - printerName string
func (_e *MockPkHelper_Expecter) ClassAddPrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassAddPrinter_Call {
return &MockPkHelper_ClassAddPrinter_Call{Call: _e.mock.On("ClassAddPrinter", className, printerName)}
}
func (_c *MockPkHelper_ClassAddPrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_ClassAddPrinter_Call) Return(_a0 error) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassAddPrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Return(run)
return _c
}
// ClassDelete provides a mock function with given fields: className
func (_m *MockPkHelper) ClassDelete(className string) error {
ret := _m.Called(className)
if len(ret) == 0 {
panic("no return value specified for ClassDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(className)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDelete'
type MockPkHelper_ClassDelete_Call struct {
*mock.Call
}
// ClassDelete is a helper method to define mock.On call
// - className string
func (_e *MockPkHelper_Expecter) ClassDelete(className interface{}) *MockPkHelper_ClassDelete_Call {
return &MockPkHelper_ClassDelete_Call{Call: _e.mock.On("ClassDelete", className)}
}
func (_c *MockPkHelper_ClassDelete_Call) Run(run func(className string)) *MockPkHelper_ClassDelete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockPkHelper_ClassDelete_Call) Return(_a0 error) *MockPkHelper_ClassDelete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_ClassDelete_Call {
_c.Call.Return(run)
return _c
}
// ClassDeletePrinter provides a mock function with given fields: className, printerName
func (_m *MockPkHelper) ClassDeletePrinter(className string, printerName string) error {
ret := _m.Called(className, printerName)
if len(ret) == 0 {
panic("no return value specified for ClassDeletePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(className, printerName)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassDeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDeletePrinter'
type MockPkHelper_ClassDeletePrinter_Call struct {
*mock.Call
}
// ClassDeletePrinter is a helper method to define mock.On call
// - className string
// - printerName string
func (_e *MockPkHelper_Expecter) ClassDeletePrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassDeletePrinter_Call {
return &MockPkHelper_ClassDeletePrinter_Call{Call: _e.mock.On("ClassDeletePrinter", className, printerName)}
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) Return(_a0 error) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Return(run)
return _c
}
// DevicesGet provides a mock function with given fields: timeout, limit, includeSchemes, excludeSchemes
func (_m *MockPkHelper) DevicesGet(timeout int, limit int, includeSchemes []string, excludeSchemes []string) ([]cups.Device, error) {
ret := _m.Called(timeout, limit, includeSchemes, excludeSchemes)
if len(ret) == 0 {
panic("no return value specified for DevicesGet")
}
var r0 []cups.Device
var r1 error
if rf, ok := ret.Get(0).(func(int, int, []string, []string) ([]cups.Device, error)); ok {
return rf(timeout, limit, includeSchemes, excludeSchemes)
}
if rf, ok := ret.Get(0).(func(int, int, []string, []string) []cups.Device); ok {
r0 = rf(timeout, limit, includeSchemes, excludeSchemes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]cups.Device)
}
}
if rf, ok := ret.Get(1).(func(int, int, []string, []string) error); ok {
r1 = rf(timeout, limit, includeSchemes, excludeSchemes)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockPkHelper_DevicesGet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DevicesGet'
type MockPkHelper_DevicesGet_Call struct {
*mock.Call
}
// DevicesGet is a helper method to define mock.On call
// - timeout int
// - limit int
// - includeSchemes []string
// - excludeSchemes []string
func (_e *MockPkHelper_Expecter) DevicesGet(timeout interface{}, limit interface{}, includeSchemes interface{}, excludeSchemes interface{}) *MockPkHelper_DevicesGet_Call {
return &MockPkHelper_DevicesGet_Call{Call: _e.mock.On("DevicesGet", timeout, limit, includeSchemes, excludeSchemes)}
}
func (_c *MockPkHelper_DevicesGet_Call) Run(run func(timeout int, limit int, includeSchemes []string, excludeSchemes []string)) *MockPkHelper_DevicesGet_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(int), args[2].([]string), args[3].([]string))
})
return _c
}
func (_c *MockPkHelper_DevicesGet_Call) Return(_a0 []cups.Device, _a1 error) *MockPkHelper_DevicesGet_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockPkHelper_DevicesGet_Call) RunAndReturn(run func(int, int, []string, []string) ([]cups.Device, error)) *MockPkHelper_DevicesGet_Call {
_c.Call.Return(run)
return _c
}
// JobCancelPurge provides a mock function with given fields: jobID, purge
func (_m *MockPkHelper) JobCancelPurge(jobID int, purge bool) error {
ret := _m.Called(jobID, purge)
if len(ret) == 0 {
panic("no return value specified for JobCancelPurge")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, bool) error); ok {
r0 = rf(jobID, purge)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobCancelPurge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobCancelPurge'
type MockPkHelper_JobCancelPurge_Call struct {
*mock.Call
}
// JobCancelPurge is a helper method to define mock.On call
// - jobID int
// - purge bool
func (_e *MockPkHelper_Expecter) JobCancelPurge(jobID interface{}, purge interface{}) *MockPkHelper_JobCancelPurge_Call {
return &MockPkHelper_JobCancelPurge_Call{Call: _e.mock.On("JobCancelPurge", jobID, purge)}
}
func (_c *MockPkHelper_JobCancelPurge_Call) Run(run func(jobID int, purge bool)) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_JobCancelPurge_Call) Return(_a0 error) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobCancelPurge_Call) RunAndReturn(run func(int, bool) error) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Return(run)
return _c
}
// JobRestart provides a mock function with given fields: jobID
func (_m *MockPkHelper) JobRestart(jobID int) error {
ret := _m.Called(jobID)
if len(ret) == 0 {
panic("no return value specified for JobRestart")
}
var r0 error
if rf, ok := ret.Get(0).(func(int) error); ok {
r0 = rf(jobID)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobRestart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobRestart'
type MockPkHelper_JobRestart_Call struct {
*mock.Call
}
// JobRestart is a helper method to define mock.On call
// - jobID int
func (_e *MockPkHelper_Expecter) JobRestart(jobID interface{}) *MockPkHelper_JobRestart_Call {
return &MockPkHelper_JobRestart_Call{Call: _e.mock.On("JobRestart", jobID)}
}
func (_c *MockPkHelper_JobRestart_Call) Run(run func(jobID int)) *MockPkHelper_JobRestart_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int))
})
return _c
}
func (_c *MockPkHelper_JobRestart_Call) Return(_a0 error) *MockPkHelper_JobRestart_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobRestart_Call) RunAndReturn(run func(int) error) *MockPkHelper_JobRestart_Call {
_c.Call.Return(run)
return _c
}
// JobSetHoldUntil provides a mock function with given fields: jobID, holdUntil
func (_m *MockPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error {
ret := _m.Called(jobID, holdUntil)
if len(ret) == 0 {
panic("no return value specified for JobSetHoldUntil")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, holdUntil)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobSetHoldUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobSetHoldUntil'
type MockPkHelper_JobSetHoldUntil_Call struct {
*mock.Call
}
// JobSetHoldUntil is a helper method to define mock.On call
// - jobID int
// - holdUntil string
func (_e *MockPkHelper_Expecter) JobSetHoldUntil(jobID interface{}, holdUntil interface{}) *MockPkHelper_JobSetHoldUntil_Call {
return &MockPkHelper_JobSetHoldUntil_Call{Call: _e.mock.On("JobSetHoldUntil", jobID, holdUntil)}
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) Run(run func(jobID int, holdUntil string)) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) Return(_a0 error) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) RunAndReturn(run func(int, string) error) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Return(run)
return _c
}
// PrinterAdd provides a mock function with given fields: name, uri, ppd, info, location
func (_m *MockPkHelper) PrinterAdd(name string, uri string, ppd string, info string, location string) error {
ret := _m.Called(name, uri, ppd, info, location)
if len(ret) == 0 {
panic("no return value specified for PrinterAdd")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, string, string) error); ok {
r0 = rf(name, uri, ppd, info, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterAdd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterAdd'
type MockPkHelper_PrinterAdd_Call struct {
*mock.Call
}
// PrinterAdd is a helper method to define mock.On call
// - name string
// - uri string
// - ppd string
// - info string
// - location string
func (_e *MockPkHelper_Expecter) PrinterAdd(name interface{}, uri interface{}, ppd interface{}, info interface{}, location interface{}) *MockPkHelper_PrinterAdd_Call {
return &MockPkHelper_PrinterAdd_Call{Call: _e.mock.On("PrinterAdd", name, uri, ppd, info, location)}
}
func (_c *MockPkHelper_PrinterAdd_Call) Run(run func(name string, uri string, ppd string, info string, location string)) *MockPkHelper_PrinterAdd_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string), args[2].(string), args[3].(string), args[4].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterAdd_Call) Return(_a0 error) *MockPkHelper_PrinterAdd_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterAdd_Call) RunAndReturn(run func(string, string, string, string, string) error) *MockPkHelper_PrinterAdd_Call {
_c.Call.Return(run)
return _c
}
// PrinterDelete provides a mock function with given fields: name
func (_m *MockPkHelper) PrinterDelete(name string) error {
ret := _m.Called(name)
if len(ret) == 0 {
panic("no return value specified for PrinterDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(name)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterDelete'
type MockPkHelper_PrinterDelete_Call struct {
*mock.Call
}
// PrinterDelete is a helper method to define mock.On call
// - name string
func (_e *MockPkHelper_Expecter) PrinterDelete(name interface{}) *MockPkHelper_PrinterDelete_Call {
return &MockPkHelper_PrinterDelete_Call{Call: _e.mock.On("PrinterDelete", name)}
}
func (_c *MockPkHelper_PrinterDelete_Call) Run(run func(name string)) *MockPkHelper_PrinterDelete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterDelete_Call) Return(_a0 error) *MockPkHelper_PrinterDelete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_PrinterDelete_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetAcceptJobs provides a mock function with given fields: name, enabled, reason
func (_m *MockPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error {
ret := _m.Called(name, enabled, reason)
if len(ret) == 0 {
panic("no return value specified for PrinterSetAcceptJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool, string) error); ok {
r0 = rf(name, enabled, reason)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetAcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetAcceptJobs'
type MockPkHelper_PrinterSetAcceptJobs_Call struct {
*mock.Call
}
// PrinterSetAcceptJobs is a helper method to define mock.On call
// - name string
// - enabled bool
// - reason string
func (_e *MockPkHelper_Expecter) PrinterSetAcceptJobs(name interface{}, enabled interface{}, reason interface{}) *MockPkHelper_PrinterSetAcceptJobs_Call {
return &MockPkHelper_PrinterSetAcceptJobs_Call{Call: _e.mock.On("PrinterSetAcceptJobs", name, enabled, reason)}
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Run(run func(name string, enabled bool, reason string)) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool), args[2].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Return(_a0 error) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) RunAndReturn(run func(string, bool, string) error) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetEnabled provides a mock function with given fields: name, enabled
func (_m *MockPkHelper) PrinterSetEnabled(name string, enabled bool) error {
ret := _m.Called(name, enabled)
if len(ret) == 0 {
panic("no return value specified for PrinterSetEnabled")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(name, enabled)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetEnabled'
type MockPkHelper_PrinterSetEnabled_Call struct {
*mock.Call
}
// PrinterSetEnabled is a helper method to define mock.On call
// - name string
// - enabled bool
func (_e *MockPkHelper_Expecter) PrinterSetEnabled(name interface{}, enabled interface{}) *MockPkHelper_PrinterSetEnabled_Call {
return &MockPkHelper_PrinterSetEnabled_Call{Call: _e.mock.On("PrinterSetEnabled", name, enabled)}
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) Run(run func(name string, enabled bool)) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) Return(_a0 error) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetInfo provides a mock function with given fields: name, info
func (_m *MockPkHelper) PrinterSetInfo(name string, info string) error {
ret := _m.Called(name, info)
if len(ret) == 0 {
panic("no return value specified for PrinterSetInfo")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(name, info)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetInfo'
type MockPkHelper_PrinterSetInfo_Call struct {
*mock.Call
}
// PrinterSetInfo is a helper method to define mock.On call
// - name string
// - info string
func (_e *MockPkHelper_Expecter) PrinterSetInfo(name interface{}, info interface{}) *MockPkHelper_PrinterSetInfo_Call {
return &MockPkHelper_PrinterSetInfo_Call{Call: _e.mock.On("PrinterSetInfo", name, info)}
}
func (_c *MockPkHelper_PrinterSetInfo_Call) Run(run func(name string, info string)) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetInfo_Call) Return(_a0 error) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetInfo_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetLocation provides a mock function with given fields: name, location
func (_m *MockPkHelper) PrinterSetLocation(name string, location string) error {
ret := _m.Called(name, location)
if len(ret) == 0 {
panic("no return value specified for PrinterSetLocation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(name, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetLocation'
type MockPkHelper_PrinterSetLocation_Call struct {
*mock.Call
}
// PrinterSetLocation is a helper method to define mock.On call
// - name string
// - location string
func (_e *MockPkHelper_Expecter) PrinterSetLocation(name interface{}, location interface{}) *MockPkHelper_PrinterSetLocation_Call {
return &MockPkHelper_PrinterSetLocation_Call{Call: _e.mock.On("PrinterSetLocation", name, location)}
}
func (_c *MockPkHelper_PrinterSetLocation_Call) Run(run func(name string, location string)) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetLocation_Call) Return(_a0 error) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetLocation_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetShared provides a mock function with given fields: name, shared
func (_m *MockPkHelper) PrinterSetShared(name string, shared bool) error {
ret := _m.Called(name, shared)
if len(ret) == 0 {
panic("no return value specified for PrinterSetShared")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(name, shared)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetShared'
type MockPkHelper_PrinterSetShared_Call struct {
*mock.Call
}
// PrinterSetShared is a helper method to define mock.On call
// - name string
// - shared bool
func (_e *MockPkHelper_Expecter) PrinterSetShared(name interface{}, shared interface{}) *MockPkHelper_PrinterSetShared_Call {
return &MockPkHelper_PrinterSetShared_Call{Call: _e.mock.On("PrinterSetShared", name, shared)}
}
func (_c *MockPkHelper_PrinterSetShared_Call) Run(run func(name string, shared bool)) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_PrinterSetShared_Call) Return(_a0 error) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetShared_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Return(run)
return _c
}
// NewMockPkHelper creates a new instance of MockPkHelper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockPkHelper(t interface {
mock.TestingT
Cleanup(func())
}) *MockPkHelper {
mock := &MockPkHelper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -1,12 +1,34 @@
package cups
import (
"errors"
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp"
)
func isAuthError(err error) bool {
if err == nil {
return false
}
var httpErr ipp.HTTPError
if errors.As(err, &httpErr) {
return httpErr.Code == 401 || httpErr.Code == 403
}
var ippErr ipp.IPPError
if errors.As(err, &ippErr) {
return ippErr.Status == ipp.StatusErrorForbidden ||
ippErr.Status == ipp.StatusErrorNotAuthenticated ||
ippErr.Status == ipp.StatusErrorNotAuthorized
}
return false
}
func (m *Manager) GetPrinters() ([]Printer, error) {
attributes := []string{
ipp.AttributePrinterName,
@@ -21,6 +43,9 @@ func (m *Manager) GetPrinters() ([]Printer, error) {
printerAttrs, err := m.client.GetPrinters(attributes)
if err != nil {
if isNoPrintersError(err) {
return []Printer{}, nil
}
return nil, err
}
@@ -91,17 +116,289 @@ func (m *Manager) GetJobs(printerName string, whichJobs string) ([]Job, error) {
}
func (m *Manager) CancelJob(jobID int) error {
return m.client.CancelJob(jobID, false)
err := m.client.CancelJob(jobID, false)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobCancelPurge(jobID, false)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) PausePrinter(printerName string) error {
return m.client.PausePrinter(printerName)
err := m.client.PausePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetEnabled(printerName, false)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) ResumePrinter(printerName string) error {
return m.client.ResumePrinter(printerName)
err := m.client.ResumePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetEnabled(printerName, true)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) PurgeJobs(printerName string) error {
return m.client.CancelAllJob(printerName, true)
err := m.client.CancelAllJob(printerName, true)
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) GetDevices() ([]Device, error) {
if m.pkHelper != nil {
return m.pkHelper.DevicesGet(10, 0, nil, nil)
}
deviceAttrs, err := m.client.GetDevices()
if err != nil {
return nil, err
}
devices := make([]Device, 0, len(deviceAttrs))
for uri, attrs := range deviceAttrs {
device := Device{
URI: uri,
Class: getStringAttr(attrs, "device-class"),
Info: getStringAttr(attrs, "device-info"),
MakeModel: getStringAttr(attrs, "device-make-and-model"),
ID: getStringAttr(attrs, "device-id"),
Location: getStringAttr(attrs, "device-location"),
}
devices = append(devices, device)
}
return devices, nil
}
func (m *Manager) GetPPDs() ([]PPD, error) {
ppdAttrs, err := m.client.GetPPDs()
if err != nil {
return nil, err
}
ppds := make([]PPD, 0, len(ppdAttrs))
for name, attrs := range ppdAttrs {
ppd := PPD{
Name: name,
NaturalLanguage: getStringAttr(attrs, "ppd-natural-language"),
MakeModel: getStringAttr(attrs, ipp.AttributePPDMakeAndModel),
DeviceID: getStringAttr(attrs, "ppd-device-id"),
Product: getStringAttr(attrs, "ppd-product"),
PSVersion: getStringAttr(attrs, "ppd-psversion"),
Type: getStringAttr(attrs, "ppd-type"),
}
ppds = append(ppds, ppd)
}
return ppds, nil
}
func (m *Manager) GetClasses() ([]PrinterClass, error) {
attributes := []string{
ipp.AttributePrinterName,
ipp.AttributePrinterUriSupported,
ipp.AttributePrinterState,
ipp.AttributeMemberURIs,
ipp.AttributeMemberNames,
ipp.AttributePrinterLocation,
ipp.AttributePrinterInfo,
}
classAttrs, err := m.client.GetClasses(attributes)
if err != nil {
return nil, err
}
classes := make([]PrinterClass, 0, len(classAttrs))
for _, attrs := range classAttrs {
class := PrinterClass{
Name: getStringAttr(attrs, ipp.AttributePrinterName),
URI: getStringAttr(attrs, ipp.AttributePrinterUriSupported),
State: parsePrinterState(attrs),
Location: getStringAttr(attrs, ipp.AttributePrinterLocation),
Info: getStringAttr(attrs, ipp.AttributePrinterInfo),
Members: getStringSliceAttr(attrs, ipp.AttributeMemberNames),
}
classes = append(classes, class)
}
return classes, nil
}
func (m *Manager) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error {
usedPkHelper := false
err := m.client.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location)
if isAuthError(err) && m.pkHelper != nil {
if err = m.pkHelper.PrinterAdd(name, deviceURI, ppd, information, location); err != nil {
return err
}
usedPkHelper = true
} else if err != nil {
return err
}
if usedPkHelper {
m.pkHelper.PrinterSetEnabled(name, true)
m.pkHelper.PrinterSetAcceptJobs(name, true, "")
} else {
if err := m.client.ResumePrinter(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetEnabled(name, true)
}
if err := m.client.AcceptJobs(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetAcceptJobs(name, true, "")
}
}
m.RefreshState()
return nil
}
func (m *Manager) DeletePrinter(printerName string) error {
err := m.client.DeletePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterDelete(printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) AcceptJobs(printerName string) error {
err := m.client.AcceptJobs(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetAcceptJobs(printerName, true, "")
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RejectJobs(printerName string) error {
err := m.client.RejectJobs(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetAcceptJobs(printerName, false, "")
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterShared(printerName string, shared bool) error {
err := m.client.SetPrinterIsShared(printerName, shared)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetShared(printerName, shared)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterLocation(printerName, location string) error {
err := m.client.SetPrinterLocation(printerName, location)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetLocation(printerName, location)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterInfo(printerName, info string) error {
err := m.client.SetPrinterInformation(printerName, info)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetInfo(printerName, info)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) MoveJob(jobID int, destPrinter string) error {
err := m.client.MoveJob(jobID, destPrinter)
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) PrintTestPage(printerName string) (int, error) {
jobID, err := m.client.PrintTestPage(printerName, strings.NewReader(config.TestPage), len(config.TestPage))
if err == nil {
m.RefreshState()
}
return jobID, err
}
func (m *Manager) AddPrinterToClass(className, printerName string) error {
err := m.client.AddPrinterToClass(className, printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassAddPrinter(className, printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RemovePrinterFromClass(className, printerName string) error {
err := m.client.DeletePrinterFromClass(className, printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassDeletePrinter(className, printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) DeleteClass(className string) error {
err := m.client.DeleteClass(className)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassDelete(className)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RestartJob(jobID int) error {
err := m.client.RestartJob(jobID)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobRestart(jobID)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) HoldJob(jobID int, holdUntil string) error {
err := m.client.HoldJobUntil(jobID, holdUntil)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobSetHoldUntil(jobID, holdUntil)
}
if err == nil {
m.RefreshState()
}
return err
}

View File

@@ -0,0 +1,235 @@
package cups_test
import (
"testing"
mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups"
mocks_pkhelper "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups_pkhelper"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func authErr() error {
return ipp.IPPError{Status: ipp.StatusErrorForbidden}
}
func TestManager_CancelJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobCancelPurge(1, false).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.CancelJob(1))
}
func TestManager_CancelJob_PkHelperError(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(authErr())
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobCancelPurge(1, false).Return(assert.AnError)
m := cups.NewTestManager(mockClient, mockPk)
assert.Error(t, m.CancelJob(1))
}
func TestManager_PausePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetEnabled("printer1", false).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.PausePrinter("printer1"))
}
func TestManager_ResumePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetEnabled("printer1", true).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.ResumePrinter("printer1"))
}
func TestManager_GetDevices_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return([]cups.Device{
{URI: "usb://HP/LaserJet", Class: "direct"},
}, nil)
m := cups.NewTestManager(mockClient, mockPk)
got, err := m.GetDevices()
assert.NoError(t, err)
assert.Len(t, got, 1)
assert.Equal(t, "usb://HP/LaserJet", got[0].URI)
}
func TestManager_GetDevices_PkHelperError(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return(nil, assert.AnError)
m := cups.NewTestManager(mockClient, mockPk)
_, err := m.GetDevices()
assert.Error(t, err)
}
func TestManager_CreatePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterAdd("newprinter", "usb://HP", "generic.ppd", "info", "location").Return(nil)
mockPk.EXPECT().PrinterSetEnabled("newprinter", true).Return(nil)
mockPk.EXPECT().PrinterSetAcceptJobs("newprinter", true, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location"))
}
func TestManager_DeletePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterDelete("printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.DeletePrinter("printer1"))
}
func TestManager_AcceptJobs_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetAcceptJobs("printer1", true, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.AcceptJobs("printer1"))
}
func TestManager_RejectJobs_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetAcceptJobs("printer1", false, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RejectJobs("printer1"))
}
func TestManager_SetPrinterShared_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetShared("printer1", true).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterShared("printer1", true))
}
func TestManager_SetPrinterLocation_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetLocation("printer1", "Office").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterLocation("printer1", "Office"))
}
func TestManager_SetPrinterInfo_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetInfo("printer1", "Main Printer").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer"))
}
func TestManager_AddPrinterToClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassAddPrinter("office", "printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.AddPrinterToClass("office", "printer1"))
}
func TestManager_RemovePrinterFromClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassDeletePrinter("office", "printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RemovePrinterFromClass("office", "printer1"))
}
func TestManager_DeleteClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassDelete("office").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.DeleteClass("office"))
}
func TestManager_RestartJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobRestart(1).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RestartJob(1))
}
func TestManager_HoldJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobSetHoldUntil(1, "indefinite").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.HoldJob(1, "indefinite"))
}

View File

@@ -137,114 +137,30 @@ func TestManager_GetJobs(t *testing.T) {
}
func TestManager_CancelJob(t *testing.T) {
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(tt.mockErr)
mockClient.EXPECT().CancelJob(1, false).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
err := m.CancelJob(1)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.CancelJob(1))
}
func TestManager_PausePrinter(t *testing.T) {
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(tt.mockErr)
mockClient.EXPECT().PausePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
err := m.PausePrinter("printer1")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.PausePrinter("printer1"))
}
func TestManager_ResumePrinter(t *testing.T) {
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(tt.mockErr)
mockClient.EXPECT().ResumePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
err := m.ResumePrinter("printer1")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.ResumePrinter("printer1"))
}
func TestManager_PurgeJobs(t *testing.T) {
@@ -269,11 +185,12 @@ func TestManager_PurgeJobs(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelAllJob("printer1", true).Return(tt.mockErr)
m := &Manager{
client: mockClient,
if !tt.wantErr {
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
}
m := NewTestManager(mockClient, nil)
err := m.PurgeJobs("printer1")
if tt.wantErr {
assert.Error(t, err)
@@ -283,3 +200,251 @@ func TestManager_PurgeJobs(t *testing.T) {
})
}
}
func TestManager_GetDevices(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{
"usb://HP/LaserJet": {
"device-class": []ipp.Attribute{{Value: "direct"}},
"device-info": []ipp.Attribute{{Value: "HP LaserJet"}},
"device-make-and-model": []ipp.Attribute{{Value: "HP LaserJet 1020"}},
},
}, nil)
m := &Manager{client: mockClient}
got, err := m.GetDevices()
assert.NoError(t, err)
assert.Len(t, got, 1)
assert.Equal(t, "usb://HP/LaserJet", got[0].URI)
assert.Equal(t, "direct", got[0].Class)
}
func TestManager_GetPPDs(t *testing.T) {
tests := []struct {
name string
mockRet map[string]ipp.Attributes
mockErr error
want int
wantErr bool
}{
{
name: "success",
mockRet: map[string]ipp.Attributes{
"drv:///sample.drv/generic.ppd": {
"ppd-make-and-model": []ipp.Attribute{{Value: "Generic PostScript"}},
"ppd-type": []ipp.Attribute{{Value: "ppd"}},
},
},
mockErr: nil,
want: 1,
wantErr: false,
},
{
name: "error",
mockRet: nil,
mockErr: errors.New("test error"),
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetPPDs().Return(tt.mockRet, tt.mockErr)
m := &Manager{client: mockClient}
got, err := m.GetPPDs()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, len(got))
})
}
}
func TestManager_GetClasses(t *testing.T) {
tests := []struct {
name string
mockRet map[string]ipp.Attributes
mockErr error
want int
wantErr bool
}{
{
name: "success",
mockRet: map[string]ipp.Attributes{
"office": {
ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}},
ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}},
ipp.AttributeMemberNames: []ipp.Attribute{{Value: "printer1"}, {Value: "printer2"}},
},
},
mockErr: nil,
want: 1,
wantErr: false,
},
{
name: "error",
mockRet: nil,
mockErr: errors.New("test error"),
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetClasses(mock.Anything).Return(tt.mockRet, tt.mockErr)
m := &Manager{client: mockClient}
got, err := m.GetClasses()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, len(got))
if len(got) > 0 {
assert.Equal(t, "office", got[0].Name)
assert.Equal(t, 2, len(got[0].Members))
}
})
}
}
func TestManager_CreatePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(nil)
mockClient.EXPECT().ResumePrinter("newprinter").Return(nil)
mockClient.EXPECT().AcceptJobs("newprinter").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location"))
}
func TestManager_DeletePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.DeletePrinter("printer1"))
}
func TestManager_AcceptJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.AcceptJobs("printer1"))
}
func TestManager_RejectJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RejectJobs("printer1"))
}
func TestManager_SetPrinterShared(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterShared("printer1", true))
}
func TestManager_SetPrinterLocation(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterLocation("printer1", "Office"))
}
func TestManager_SetPrinterInfo(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer"))
}
func TestManager_MoveJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().MoveJob(1, "printer2").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
err := m.MoveJob(1, "printer2")
assert.NoError(t, err)
}
func TestManager_PrintTestPage(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
jobID, err := m.PrintTestPage("printer1")
assert.NoError(t, err)
assert.Equal(t, 42, jobID)
}
func TestManager_AddPrinterToClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.AddPrinterToClass("office", "printer1"))
}
func TestManager_RemovePrinterFromClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RemovePrinterFromClass("office", "printer1"))
}
func TestManager_DeleteClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.DeleteClass("office"))
}
func TestManager_RestartJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RestartJob(1))
}
func TestManager_HoldJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.HoldJob(1, "indefinite"))
}

View File

@@ -40,6 +40,40 @@ func HandleRequest(conn net.Conn, req Request, manager *Manager) {
handleCancelJob(conn, req, manager)
case "cups.purgeJobs":
handlePurgeJobs(conn, req, manager)
case "cups.getDevices":
handleGetDevices(conn, req, manager)
case "cups.getPPDs":
handleGetPPDs(conn, req, manager)
case "cups.getClasses":
handleGetClasses(conn, req, manager)
case "cups.createPrinter":
handleCreatePrinter(conn, req, manager)
case "cups.deletePrinter":
handleDeletePrinter(conn, req, manager)
case "cups.acceptJobs":
handleAcceptJobs(conn, req, manager)
case "cups.rejectJobs":
handleRejectJobs(conn, req, manager)
case "cups.setPrinterShared":
handleSetPrinterShared(conn, req, manager)
case "cups.setPrinterLocation":
handleSetPrinterLocation(conn, req, manager)
case "cups.setPrinterInfo":
handleSetPrinterInfo(conn, req, manager)
case "cups.moveJob":
handleMoveJob(conn, req, manager)
case "cups.printTestPage":
handlePrintTestPage(conn, req, manager)
case "cups.addPrinterToClass":
handleAddPrinterToClass(conn, req, manager)
case "cups.removePrinterFromClass":
handleRemovePrinterFromClass(conn, req, manager)
case "cups.deleteClass":
handleDeleteClass(conn, req, manager)
case "cups.restartJob":
handleRestartJob(conn, req, manager)
case "cups.holdJob":
handleHoldJob(conn, req, manager)
default:
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
}
@@ -158,3 +192,291 @@ func handleSubscribe(conn net.Conn, req Request, manager *Manager) {
}
}
}
func handleGetDevices(conn net.Conn, req Request, manager *Manager) {
devices, err := manager.GetDevices()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, devices)
}
func handleGetPPDs(conn net.Conn, req Request, manager *Manager) {
ppds, err := manager.GetPPDs()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, ppds)
}
func handleGetClasses(conn net.Conn, req Request, manager *Manager) {
classes, err := manager.GetClasses()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, classes)
}
func handleCreatePrinter(conn net.Conn, req Request, manager *Manager) {
name, ok := req.Params["name"].(string)
if !ok || name == "" {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
return
}
deviceURI, ok := req.Params["deviceURI"].(string)
if !ok || deviceURI == "" {
models.RespondError(conn, req.ID, "missing or invalid 'deviceURI' parameter")
return
}
ppd, ok := req.Params["ppd"].(string)
if !ok || ppd == "" {
models.RespondError(conn, req.ID, "missing or invalid 'ppd' parameter")
return
}
shared, _ := req.Params["shared"].(bool)
errorPolicy, _ := req.Params["errorPolicy"].(string)
information, _ := req.Params["information"].(string)
location, _ := req.Params["location"].(string)
if err := manager.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer created"})
}
func handleDeletePrinter(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.DeletePrinter(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer deleted"})
}
func handleAcceptJobs(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.AcceptJobs(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "accepting jobs"})
}
func handleRejectJobs(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.RejectJobs(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "rejecting jobs"})
}
func handleSetPrinterShared(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
shared, ok := req.Params["shared"].(bool)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'shared' parameter")
return
}
if err := manager.SetPrinterShared(printerName, shared); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "sharing updated"})
}
func handleSetPrinterLocation(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
location, ok := req.Params["location"].(string)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'location' parameter")
return
}
if err := manager.SetPrinterLocation(printerName, location); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "location updated"})
}
func handleSetPrinterInfo(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
info, ok := req.Params["info"].(string)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'info' parameter")
return
}
if err := manager.SetPrinterInfo(printerName, info); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "info updated"})
}
func handleMoveJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
destPrinter, ok := req.Params["destPrinter"].(string)
if !ok || destPrinter == "" {
models.RespondError(conn, req.ID, "missing or invalid 'destPrinter' parameter")
return
}
if err := manager.MoveJob(int(jobIDFloat), destPrinter); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job moved"})
}
type TestPageResult struct {
Success bool `json:"success"`
JobID int `json:"jobId"`
Message string `json:"message"`
}
func handlePrintTestPage(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
jobID, err := manager.PrintTestPage(printerName)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, TestPageResult{Success: true, JobID: jobID, Message: "test page queued"})
}
func handleAddPrinterToClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.AddPrinterToClass(className, printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer added to class"})
}
func handleRemovePrinterFromClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.RemovePrinterFromClass(className, printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer removed from class"})
}
func handleDeleteClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
if err := manager.DeleteClass(className); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "class deleted"})
}
func handleRestartJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
if err := manager.RestartJob(int(jobIDFloat)); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job restarted"})
}
func handleHoldJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
holdUntil, _ := req.Params["holdUntil"].(string)
if holdUntil == "" {
holdUntil = "indefinite"
}
if err := manager.HoldJob(int(jobIDFloat), holdUntil); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job held"})
}

View File

@@ -145,10 +145,9 @@ func TestHandleGetJobs_MissingParam(t *testing.T) {
func TestHandlePausePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -173,10 +172,9 @@ func TestHandlePausePrinter(t *testing.T) {
func TestHandleResumePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -201,10 +199,9 @@ func TestHandleResumePrinter(t *testing.T) {
func TestHandleCancelJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -229,10 +226,9 @@ func TestHandleCancelJob(t *testing.T) {
func TestHandlePurgeJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelAllJob("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := &Manager{
client: mockClient,
}
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -277,3 +273,439 @@ func TestHandleRequest_UnknownMethod(t *testing.T) {
assert.Nil(t, resp.Result)
assert.NotNil(t, resp.Error)
}
func TestHandleGetDevices(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{
"usb://HP/LaserJet": {
"device-class": []ipp.Attribute{{Value: "direct"}},
"device-info": []ipp.Attribute{{Value: "HP LaserJet"}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getDevices"}
handleGetDevices(conn, req, m)
var resp models.Response[[]Device]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleGetPPDs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetPPDs().Return(map[string]ipp.Attributes{
"generic.ppd": {
"ppd-make-and-model": []ipp.Attribute{{Value: "Generic"}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getPPDs"}
handleGetPPDs(conn, req, m)
var resp models.Response[[]PPD]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleGetClasses(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetClasses(mock.Anything).Return(map[string]ipp.Attributes{
"office": {
ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}},
ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getClasses"}
handleGetClasses(conn, req, m)
var resp models.Response[[]PrinterClass]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleCreatePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", false, "", "", "").Return(nil)
mockClient.EXPECT().ResumePrinter("newprinter").Return(nil)
mockClient.EXPECT().AcceptJobs("newprinter").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.createPrinter",
Params: map[string]interface{}{
"name": "newprinter",
"deviceURI": "usb://HP",
"ppd": "generic.ppd",
},
}
handleCreatePrinter(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleCreatePrinter_MissingParams(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.createPrinter", Params: map[string]interface{}{}}
handleCreatePrinter(conn, req, m)
var resp models.Response[interface{}]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.Nil(t, resp.Result)
assert.NotNil(t, resp.Error)
}
func TestHandleDeletePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.deletePrinter",
Params: map[string]interface{}{"printerName": "printer1"},
}
handleDeletePrinter(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleAcceptJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.acceptJobs",
Params: map[string]interface{}{"printerName": "printer1"},
}
handleAcceptJobs(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRejectJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.rejectJobs",
Params: map[string]interface{}{"printerName": "printer1"},
}
handleRejectJobs(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterShared(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterShared",
Params: map[string]interface{}{"printerName": "printer1", "shared": true},
}
handleSetPrinterShared(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterLocation(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterLocation",
Params: map[string]interface{}{"printerName": "printer1", "location": "Office"},
}
handleSetPrinterLocation(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterInfo(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterInfo",
Params: map[string]interface{}{"printerName": "printer1", "info": "Main Printer"},
}
handleSetPrinterInfo(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleMoveJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().MoveJob(1, "printer2").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.moveJob",
Params: map[string]interface{}{"jobID": float64(1), "destPrinter": "printer2"},
}
handleMoveJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandlePrintTestPage(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.printTestPage",
Params: map[string]interface{}{"printerName": "printer1"},
}
handlePrintTestPage(conn, req, m)
var resp models.Response[TestPageResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
assert.Equal(t, 42, resp.Result.JobID)
}
func TestHandleAddPrinterToClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.addPrinterToClass",
Params: map[string]interface{}{"className": "office", "printerName": "printer1"},
}
handleAddPrinterToClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRemovePrinterFromClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.removePrinterFromClass",
Params: map[string]interface{}{"className": "office", "printerName": "printer1"},
}
handleRemovePrinterFromClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleDeleteClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.deleteClass",
Params: map[string]interface{}{"className": "office"},
}
handleDeleteClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRestartJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.restartJob",
Params: map[string]interface{}{"jobID": float64(1)},
}
handleRestartJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleHoldJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.holdJob",
Params: map[string]interface{}{"jobID": float64(1)},
}
handleHoldJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleHoldJob_WithHoldUntil(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "no-hold").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.holdJob",
Params: map[string]interface{}{"jobID": float64(1), "holdUntil": "no-hold"},
}
handleHoldJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}

View File

@@ -1,6 +1,7 @@
package cups
import (
"errors"
"fmt"
"os"
"strconv"
@@ -31,11 +32,21 @@ func NewManager() (*Manager, error) {
client := ipp.NewCUPSClient(host, port, username, password, false)
baseURL := fmt.Sprintf("http://%s:%d", host, port)
var pkHelper PkHelper
if isLocalCUPS(host) {
var err error
pkHelper, err = NewPkHelper()
if err != nil {
log.Warnf("[CUPS] Failed to initialize pkhelper: %v", err)
}
}
m := &Manager{
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: client,
pkHelper: pkHelper,
baseURL: baseURL,
stateMutex: sync.RWMutex{},
stopChan: make(chan struct{}),
@@ -98,6 +109,12 @@ func (m *Manager) eventHandler() {
func (m *Manager) updateState() error {
printers, err := m.GetPrinters()
if err != nil {
if isNoPrintersError(err) {
m.stateMutex.Lock()
m.state.Printers = make(map[string]*Printer)
m.stateMutex.Unlock()
return nil
}
return err
}
@@ -119,6 +136,19 @@ func (m *Manager) updateState() error {
return nil
}
func isNoPrintersError(err error) bool {
if err == nil {
return false
}
var ippErr ipp.IPPError
if errors.As(err, &ippErr) {
return ippErr.Status == 1030
}
return false
}
func (m *Manager) notifier() {
defer m.notifierWg.Done()
const minGap = 100 * time.Millisecond
@@ -170,6 +200,14 @@ func (m *Manager) notifySubscribers() {
}
}
func (m *Manager) RefreshState() {
if err := m.updateState(); err != nil {
log.Warnf("[CUPS] Failed to refresh state: %v", err)
return
}
m.notifySubscribers()
}
func (m *Manager) GetState() CUPSState {
return m.snapshotState()
}
@@ -256,6 +294,7 @@ func stateChanged(old, new *CUPSState) bool {
}
if oldPrinter.State != newPrinter.State ||
oldPrinter.StateReason != newPrinter.StateReason ||
oldPrinter.Accepting != newPrinter.Accepting ||
len(oldPrinter.Jobs) != len(newPrinter.Jobs) {
return true
}
@@ -334,3 +373,18 @@ func getBoolAttr(attrs ipp.Attributes, key string) bool {
}
return false
}
func getStringSliceAttr(attrs ipp.Attributes, key string) []string {
attr, ok := attrs[key]
if !ok {
return nil
}
result := make([]string, 0, len(attr))
for _, a := range attr {
if val, ok := a.Value.(string); ok {
result = append(result, val)
}
}
return result
}

View File

@@ -0,0 +1,184 @@
package cups
import (
"fmt"
"strings"
"github.com/godbus/dbus/v5"
)
const (
pkHelperDest = "org.opensuse.CupsPkHelper.Mechanism"
pkHelperPath = "/"
pkHelperInterface = "org.opensuse.CupsPkHelper.Mechanism"
)
type PkHelper interface {
DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error)
PrinterAdd(name, uri, ppd, info, location string) error
PrinterDelete(name string) error
PrinterSetEnabled(name string, enabled bool) error
PrinterSetAcceptJobs(name string, enabled bool, reason string) error
PrinterSetInfo(name, info string) error
PrinterSetLocation(name, location string) error
PrinterSetShared(name string, shared bool) error
ClassAddPrinter(className, printerName string) error
ClassDeletePrinter(className, printerName string) error
ClassDelete(className string) error
JobCancelPurge(jobID int, purge bool) error
JobRestart(jobID int) error
JobSetHoldUntil(jobID int, holdUntil string) error
}
type DBusPkHelper struct {
conn *dbus.Conn
obj dbus.BusObject
}
func NewPkHelper() (*DBusPkHelper, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
}
return &DBusPkHelper{
conn: conn,
obj: conn.Object(pkHelperDest, pkHelperPath),
}, nil
}
func (p *DBusPkHelper) DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error) {
if includeSchemes == nil {
includeSchemes = []string{}
}
if excludeSchemes == nil {
excludeSchemes = []string{}
}
var errStr string
var devicesMap map[string]string
call := p.obj.Call(pkHelperInterface+".DevicesGet", 0, int32(timeout), int32(limit), includeSchemes, excludeSchemes)
if call.Err != nil {
return nil, call.Err
}
if err := call.Store(&errStr, &devicesMap); err != nil {
return nil, err
}
if errStr != "" {
return nil, fmt.Errorf("%s", errStr)
}
return parseDevicesMap(devicesMap), nil
}
func parseDevicesMap(devicesMap map[string]string) []Device {
devicesByIndex := make(map[string]*Device)
for key, value := range devicesMap {
idx := strings.LastIndex(key, ":")
if idx == -1 {
continue
}
attr := key[:idx]
index := key[idx+1:]
dev, ok := devicesByIndex[index]
if !ok {
dev = &Device{}
devicesByIndex[index] = dev
}
switch attr {
case "device-uri":
dev.URI = value
case "device-class":
dev.Class = value
case "device-info":
dev.Info = value
case "device-make-and-model":
dev.MakeModel = value
case "device-id":
dev.ID = value
case "device-location":
dev.Location = value
}
}
devices := make([]Device, 0, len(devicesByIndex))
for _, dev := range devicesByIndex {
if dev.URI != "" {
devices = append(devices, *dev)
}
}
return devices
}
func (p *DBusPkHelper) PrinterAdd(name, uri, ppd, info, location string) error {
return p.callSimple("PrinterAdd", name, uri, ppd, info, location)
}
func (p *DBusPkHelper) PrinterDelete(name string) error {
return p.callSimple("PrinterDelete", name)
}
func (p *DBusPkHelper) PrinterSetEnabled(name string, enabled bool) error {
return p.callSimple("PrinterSetEnabled", name, enabled)
}
func (p *DBusPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error {
return p.callSimple("PrinterSetAcceptJobs", name, enabled, reason)
}
func (p *DBusPkHelper) PrinterSetInfo(name, info string) error {
return p.callSimple("PrinterSetInfo", name, info)
}
func (p *DBusPkHelper) PrinterSetLocation(name, location string) error {
return p.callSimple("PrinterSetLocation", name, location)
}
func (p *DBusPkHelper) PrinterSetShared(name string, shared bool) error {
return p.callSimple("PrinterSetShared", name, shared)
}
func (p *DBusPkHelper) ClassAddPrinter(className, printerName string) error {
return p.callSimple("ClassAddPrinter", className, printerName)
}
func (p *DBusPkHelper) ClassDeletePrinter(className, printerName string) error {
return p.callSimple("ClassDeletePrinter", className, printerName)
}
func (p *DBusPkHelper) ClassDelete(className string) error {
return p.callSimple("ClassDelete", className)
}
func (p *DBusPkHelper) JobCancelPurge(jobID int, purge bool) error {
return p.callSimple("JobCancelPurge", int32(jobID), purge)
}
func (p *DBusPkHelper) JobRestart(jobID int) error {
return p.callSimple("JobRestart", int32(jobID))
}
func (p *DBusPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error {
return p.callSimple("JobSetHoldUntil", int32(jobID), holdUntil)
}
func (p *DBusPkHelper) callSimple(method string, args ...interface{}) error {
var errStr string
call := p.obj.Call(pkHelperInterface+"."+method, 0, args...)
if call.Err != nil {
return call.Err
}
if err := call.Store(&errStr); err != nil {
return err
}
if errStr != "" {
return fmt.Errorf("%s", errStr)
}
return nil
}

View File

@@ -0,0 +1,95 @@
package cups
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseDevicesMap(t *testing.T) {
tests := []struct {
name string
input map[string]string
wantLen int
wantURIs []string
}{
{
name: "empty",
input: map[string]string{},
wantLen: 0,
wantURIs: nil,
},
{
name: "single_device",
input: map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-make-and-model:0": "HP LaserJet 1020",
"device-id:0": "MFG:HP;MDL:LaserJet",
},
wantLen: 1,
wantURIs: []string{"usb://HP/LaserJet"},
},
{
name: "multiple_devices",
input: map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-uri:1": "socket://192.168.1.100",
"device-class:1": "network",
"device-info:1": "Network Printer",
},
wantLen: 2,
wantURIs: []string{"usb://HP/LaserJet", "socket://192.168.1.100"},
},
{
name: "malformed_key",
input: map[string]string{
"no-colon-here": "value",
},
wantLen: 0,
wantURIs: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := parseDevicesMap(tt.input)
assert.Len(t, got, tt.wantLen)
if tt.wantURIs != nil {
gotURIs := make(map[string]bool)
for _, d := range got {
gotURIs[d.URI] = true
}
for _, uri := range tt.wantURIs {
assert.True(t, gotURIs[uri], "expected URI %s not found", uri)
}
}
})
}
}
func TestParseDevicesMap_Attributes(t *testing.T) {
input := map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-make-and-model:0": "HP LaserJet 1020",
"device-id:0": "MFG:HP;MDL:LaserJet",
"device-location:0": "USB Port",
}
got := parseDevicesMap(input)
assert.Len(t, got, 1)
dev := got[0]
assert.Equal(t, "usb://HP/LaserJet", dev.URI)
assert.Equal(t, "direct", dev.Class)
assert.Equal(t, "HP LaserJet", dev.Info)
assert.Equal(t, "HP LaserJet 1020", dev.MakeModel)
assert.Equal(t, "MFG:HP;MDL:LaserJet", dev.ID)
assert.Equal(t, "USB Port", dev.Location)
}

View File

@@ -0,0 +1,13 @@
package cups
func NewTestManager(client CUPSClientInterface, pkHelper PkHelper) *Manager {
return &Manager{
client: client,
pkHelper: pkHelper,
state: &CUPSState{
Printers: make(map[string]*Printer),
},
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
}

View File

@@ -35,9 +35,38 @@ type Job struct {
TimeCreated time.Time `json:"timeCreated"`
}
type Device struct {
URI string `json:"uri"`
Class string `json:"class"`
Info string `json:"info"`
MakeModel string `json:"makeModel"`
ID string `json:"id"`
Location string `json:"location"`
}
type PPD struct {
Name string `json:"name"`
NaturalLanguage string `json:"naturalLanguage"`
MakeModel string `json:"makeModel"`
DeviceID string `json:"deviceId"`
Product string `json:"product"`
PSVersion string `json:"psVersion"`
Type string `json:"type"`
}
type PrinterClass struct {
Name string `json:"name"`
URI string `json:"uri"`
State string `json:"state"`
Members []string `json:"members"`
Location string `json:"location"`
Info string `json:"info"`
}
type Manager struct {
state *CUPSState
client CUPSClientInterface
pkHelper PkHelper
subscription SubscriptionManagerInterface
stateMutex sync.RWMutex
subscribers syncmap.Map[string, chan CUPSState]
@@ -63,6 +92,24 @@ type CUPSClientInterface interface {
ResumePrinter(printer string) error
CancelAllJob(printer string, purge bool) error
SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error)
GetDevices() (map[string]ipp.Attributes, error)
GetPPDs() (map[string]ipp.Attributes, error)
GetClasses(attributes []string) (map[string]ipp.Attributes, error)
CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error
DeletePrinter(printer string) error
AcceptJobs(printer string) error
RejectJobs(printer string) error
SetPrinterIsShared(printer string, shared bool) error
SetPrinterLocation(printer, location string) error
SetPrinterInformation(printer, information string) error
MoveJob(jobID int, destPrinter string) error
PrintTestPage(printer string, testPageData io.Reader, size int) (int, error)
AddPrinterToClass(class, printer string) error
DeletePrinterFromClass(class, printer string) error
DeleteClass(class string) error
RestartJob(jobID int) error
HoldJobUntil(jobID int, holdUntil string) error
}
type SubscriptionEvent struct {

View File

@@ -31,7 +31,9 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
)
const APIVersion = 21
const APIVersion = 22
var CLIVersion = "dev"
type Capabilities struct {
Capabilities []string `json:"capabilities"`
@@ -39,6 +41,7 @@ type Capabilities struct {
type ServerInfo struct {
APIVersion int `json:"apiVersion"`
CLIVersion string `json:"cliVersion,omitempty"`
Capabilities []string `json:"capabilities"`
}
@@ -431,6 +434,7 @@ func getServerInfo() ServerInfo {
return ServerInfo{
APIVersion: APIVersion,
CLIVersion: CLIVersion,
Capabilities: caps,
}
}

View File

@@ -288,6 +288,7 @@ const (
// useful mime types for ipp
const (
MimeTypePostscript = "application/postscript"
MimeTypePDF = "application/pdf"
MimeTypeOctetStream = "application/octet-stream"
)

View File

@@ -1,7 +1,7 @@
package ipp
import (
"bytes"
"io"
"strings"
)
@@ -300,22 +300,13 @@ func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, err
return printerNameMap, nil
}
// PrintTestPage prints a test page of type application/vnd.cups-pdf-banner
func (c *CUPSClient) PrintTestPage(printer string) (int, error) {
testPage := new(bytes.Buffer)
testPage.WriteString("#PDF-BANNER\n")
testPage.WriteString("Template default-testpage.pdf\n")
testPage.WriteString("Show printer-name printer-info printer-location printer-make-and-model printer-driver-name")
testPage.WriteString("printer-driver-version paper-size imageable-area job-id options time-at-creation")
testPage.WriteString("time-at-processing\n\n")
return c.PrintDocuments([]Document{
{
Document: testPage,
// PrintTestPage prints a test page using the provided PDF data
func (c *CUPSClient) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) {
return c.PrintJob(Document{
Document: testPageData,
Name: "Test Page",
Size: testPage.Len(),
MimeType: MimeTypePostscript,
},
Size: size,
MimeType: MimeTypePDF,
}, printer, map[string]interface{}{
AttributeJobName: "Test Page",
})

View File

@@ -146,13 +146,30 @@ FocusScope {
}
Loader {
id: launcherLoader
id: printerLoader
anchors.fill: parent
active: root.currentIndex === 7
visible: active
focus: active
sourceComponent: PrinterTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
id: launcherLoader
anchors.fill: parent
active: root.currentIndex === 8
visible: active
focus: active
sourceComponent: LauncherTab {}
onActiveChanged: {
@@ -166,7 +183,7 @@ FocusScope {
id: themeColorsLoader
anchors.fill: parent
active: root.currentIndex === 8
active: root.currentIndex === 9
visible: active
focus: active
@@ -183,7 +200,7 @@ FocusScope {
id: powerLoader
anchors.fill: parent
active: root.currentIndex === 9
active: root.currentIndex === 10
visible: active
focus: active
@@ -200,7 +217,7 @@ FocusScope {
id: pluginsLoader
anchors.fill: parent
active: root.currentIndex === 10
active: root.currentIndex === 11
visible: active
focus: active
@@ -219,7 +236,7 @@ FocusScope {
id: aboutLoader
anchors.fill: parent
active: root.currentIndex === 11
active: root.currentIndex === 12
visible: active
focus: active

View File

@@ -67,6 +67,15 @@ FloatingWindow {
}
}
Loader {
active: settingsModal.visible
sourceComponent: Component {
Ref {
service: CupsService
}
}
}
FileBrowserModal {
id: profileBrowser
@@ -112,7 +121,7 @@ FloatingWindow {
focus: true
Keys.onPressed: event => {
const tabCount = 12;
const tabCount = 13;
if (event.key === Qt.Key_Escape) {
hide();
event.accepted = true;

View File

@@ -48,33 +48,45 @@ Rectangle {
"dmsOnly": true,
"tabIndex": 6
},
{
"text": I18n.tr("Printers"),
"icon": "print",
"cupsOnly": true,
"tabIndex": 7
},
{
"text": I18n.tr("Launcher"),
"icon": "apps",
"tabIndex": 7
"tabIndex": 8
},
{
"text": I18n.tr("Theme & Colors"),
"icon": "palette",
"tabIndex": 8
"tabIndex": 9
},
{
"text": I18n.tr("Power & Security"),
"icon": "power",
"tabIndex": 9
"tabIndex": 10
},
{
"text": I18n.tr("Plugins"),
"icon": "extension",
"tabIndex": 10
"tabIndex": 11
},
{
"text": I18n.tr("About"),
"icon": "info",
"tabIndex": 11
"tabIndex": 12
}
]
readonly property var sidebarItems: allSidebarItems.filter(item => !item.dmsOnly || !NetworkService.usingLegacy)
readonly property var sidebarItems: allSidebarItems.filter(item => {
if (item.dmsOnly && NetworkService.usingLegacy)
return false;
if (item.cupsOnly && !CupsService.cupsAvailable)
return false;
return true;
})
function navigateNext() {
const currentItemIndex = sidebarItems.findIndex(item => item.tabIndex === currentIndex);

View File

@@ -41,16 +41,10 @@ QtObject {
onItemChanged: {
root.cupsBuiltinInstance = item;
if (item && !DMSService.activeSubscriptions.includes("cups") && !DMSService.activeSubscriptions.includes("all")) {
DMSService.addSubscription("cups");
}
}
onActiveChanged: {
if (!active) {
if (DMSService.activeSubscriptions.includes("cups")) {
DMSService.removeSubscription("cups");
}
root.cupsBuiltinInstance = null;
}
}

View File

@@ -22,6 +22,15 @@ BasePill {
property bool showBatteryIcon: SettingsData.controlCenterShowBatteryIcon
property bool showPrinterIcon: SettingsData.controlCenterShowPrinterIcon
Loader {
active: root.showPrinterIcon
sourceComponent: Component {
Ref {
service: CupsService
}
}
}
function getNetworkIconName() {
if (NetworkService.wifiToggling)
return "sync";

View File

@@ -374,7 +374,7 @@ DankPopout {
active: root.currentTabIndex === 2
tabBarItem: tabBar
keyForwardTarget: mainContainer
targetScreen: root.triggerScreen
targetScreen: root.screen
parentPopout: root
}

View File

@@ -3,6 +3,7 @@ import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Modals.FileBrowser
import qs.Widgets
@@ -244,6 +245,12 @@ Item {
setInitialSelection();
}
}
function onPerMonitorWallpaperChanged() {
loadWallpaperDirectory();
if (visible && active) {
setInitialSelection();
}
}
}
onTargetScreenNameChanged: {

View File

@@ -595,6 +595,159 @@ Item {
}
}
StyledRect {
visible: DMSService.isConnected
width: parent.width
height: backendSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: backendSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "dns"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Backend")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
spacing: Theme.spacingL
Column {
spacing: 2
StyledText {
text: I18n.tr("Version")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: DMSService.cliVersion || "—"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
}
}
Rectangle {
width: 1
height: 32
color: Theme.outlineVariant
}
Column {
spacing: 2
StyledText {
text: I18n.tr("API")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: `v${DMSService.apiVersion}`
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
}
}
Rectangle {
width: 1
height: 32
color: Theme.outlineVariant
}
Column {
spacing: 2
StyledText {
text: I18n.tr("Status")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
Row {
spacing: 4
Rectangle {
width: 8
height: 8
radius: 4
color: Theme.success
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Connected")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingS
visible: DMSService.capabilities.length > 0
StyledText {
text: I18n.tr("Capabilities")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
Flow {
width: parent.width
spacing: 6
Repeater {
model: DMSService.capabilities
Rectangle {
width: capText.implicitWidth + 16
height: 26
radius: 13
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
StyledText {
id: capText
anchors.centerIn: parent
text: modelData
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
}
}
}
}
}
}
}
// Support Section
StyledRect {
width: parent.width

View File

@@ -288,7 +288,7 @@ Item {
return I18n.tr("%1 connected").arg(connected);
}
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.ethernetConnected ? Theme.success : Theme.surfaceVariantText
color: NetworkService.ethernetConnected ? Theme.primary : Theme.surfaceVariantText
}
}
}
@@ -758,7 +758,7 @@ Item {
return I18n.tr("Not connected");
}
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.wifiConnected ? Theme.success : Theme.surfaceVariantText
color: NetworkService.wifiConnected ? Theme.primary : Theme.surfaceVariantText
}
}
@@ -1430,7 +1430,7 @@ Item {
return names[0] + " +" + (names.length - 1);
}
font.pixelSize: Theme.fontSizeSmall
color: DMSNetworkService.connected ? Theme.success : Theme.surfaceVariantText
color: DMSNetworkService.connected ? Theme.primary : Theme.surfaceVariantText
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,8 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
Singleton {
@@ -12,20 +10,179 @@ Singleton {
property int refCount: 0
onRefCountChanged: {
if (refCount > 0) {
ensureSubscription();
} else if (refCount === 0 && DMSService.activeSubscriptions.includes("cups")) {
DMSService.removeSubscription("cups");
}
}
function ensureSubscription() {
if (refCount <= 0)
return;
if (!DMSService.isConnected)
return;
if (DMSService.activeSubscriptions.includes("cups"))
return;
if (DMSService.activeSubscriptions.includes("all"))
return;
DMSService.addSubscription("cups");
if (cupsAvailable) {
getState();
}
}
property var printerNames: []
property var printers: []
property string selectedPrinter: ""
property string expandedPrinter: ""
property bool cupsAvailable: false
property bool stateInitialized: false
property var devices: []
property var ppds: []
property var printerClasses: []
readonly property var filteredDevices: {
if (!devices || devices.length === 0)
return [];
const bareProtocols = ["ipp", "ipps", "http", "https", "lpd", "socket", "beh", "dnssd", "mdns", "smb", "file", "cups-brf"];
// First pass: filter out invalid/bare protocol entries
const validDevices = devices.filter(d => {
if (!d.uri)
return false;
const uriLower = d.uri.toLowerCase();
for (let proto of bareProtocols) {
if (uriLower === proto || uriLower === proto + ":")
return false;
}
if (d.class === "network" && d.info === "Backend Error Handler")
return false;
return true;
});
// Second pass: prefer IPP over LPD for the same printer
// _printer._tcp (LPD) doesn't work well with driverless printing
// _ipp._tcp or _ipps._tcp (IPP) should be preferred
const ippDeviceHosts = new Set();
for (const d of validDevices) {
if (!d.uri)
continue;
// Extract hostname from dnssd URIs like dnssd://Name%20[mac]._ipp._tcp.local
const ippMatch = d.uri.match(/dnssd:\/\/[^/]*\._ipps?\._tcp/);
if (ippMatch) {
// Extract the unique identifier (usually MAC address in brackets)
const macMatch = d.uri.match(/\[([a-f0-9]+)\]/i);
if (macMatch)
ippDeviceHosts.add(macMatch[1].toLowerCase());
}
}
// Filter out _printer._tcp devices when we have _ipp._tcp for the same printer
return validDevices.filter(d => {
if (!d.uri)
return true;
// If this is an LPD device, check if we have an IPP alternative
if (d.uri.includes("._printer._tcp")) {
const macMatch = d.uri.match(/\[([a-f0-9]+)\]/i);
if (macMatch && ippDeviceHosts.has(macMatch[1].toLowerCase())) {
return false; // Skip LPD device, we have IPP
}
}
return true;
});
}
function decodeUri(str) {
if (!str)
return "";
try {
return decodeURIComponent(str.replace(/\+/g, " "));
} catch (e) {
return str;
}
}
function getDeviceDisplayName(device) {
if (!device)
return "";
if (device.info && device.info.length > 0) {
return decodeUri(device.info);
}
if (device.makeModel && device.makeModel.length > 0) {
return decodeUri(device.makeModel);
}
return decodeUri(device.uri);
}
function getDeviceSubtitle(device) {
if (!device)
return "";
const parts = [];
if (device.class) {
switch (device.class) {
case "direct":
parts.push(I18n.tr("Local"));
break;
case "network":
parts.push(I18n.tr("Network"));
break;
case "file":
parts.push(I18n.tr("File"));
break;
default:
parts.push(device.class);
}
}
if (device.location)
parts.push(decodeUri(device.location));
return parts.join(" • ");
}
function suggestPrinterName(device) {
if (!device)
return "";
let name = device.info || device.makeModel || "";
name = name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
return name.substring(0, 32) || "Printer";
}
function getMatchingPPDs(device) {
if (!device || !ppds || ppds.length === 0)
return [];
const isDnssd = device.uri && (device.uri.startsWith("dnssd://") || device.uri.startsWith("ipp://") || device.uri.startsWith("ipps://"));
if (isDnssd) {
const driverless = ppds.filter(p => p.name === "driverless" || p.name === "everywhere" || (p.makeModel && p.makeModel.toLowerCase().includes("driverless")));
if (driverless.length > 0)
return driverless;
}
if (!device.makeModel)
return [];
const makeModelLower = device.makeModel.toLowerCase();
const words = makeModelLower.split(/[\s_-]+/).filter(w => w.length > 2);
return ppds.filter(p => {
if (!p.makeModel)
return false;
const ppdLower = p.makeModel.toLowerCase();
return words.some(w => ppdLower.includes(w));
}).slice(0, 10);
}
property bool loadingDevices: false
property bool loadingPPDs: false
property bool loadingClasses: false
property bool creatingPrinter: false
signal cupsStateUpdate
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
Component.onCompleted: {
if (socketPath && socketPath.length > 0) {
checkDMSCapabilities()
checkDMSCapabilities();
}
}
@@ -34,7 +191,8 @@ Singleton {
function onConnectionStateChanged() {
if (DMSService.isConnected) {
checkDMSCapabilities()
checkDMSCapabilities();
ensureSubscription();
}
}
}
@@ -44,245 +202,558 @@ Singleton {
enabled: DMSService.isConnected
function onCupsStateUpdate(data) {
console.log("CupsService: Subscription update received")
getState()
console.log("CupsService: Subscription update received");
getState();
}
function onCapabilitiesChanged() {
checkDMSCapabilities()
checkDMSCapabilities();
}
}
function checkDMSCapabilities() {
if (!DMSService.isConnected) {
return
}
if (DMSService.capabilities.length === 0) {
return
}
cupsAvailable = DMSService.capabilities.includes("cups")
if (!DMSService.isConnected)
return;
if (DMSService.capabilities.length === 0)
return;
cupsAvailable = DMSService.capabilities.includes("cups");
if (cupsAvailable && !stateInitialized) {
stateInitialized = true
getState()
stateInitialized = true;
getState();
}
}
function getState() {
if (!cupsAvailable)
return
return;
DMSService.sendRequest("cups.getPrinters", null, response => {
if (response.result) {
updatePrinters(response.result)
fetchAllJobs()
updatePrinters(response.result);
fetchAllJobs();
}
})
});
}
function updatePrinters(printersData) {
printerNames = printersData.map(p => p.name)
printerNames = printersData.map(p => p.name);
let printersObj = {}
let printersObj = {};
for (var i = 0; i < printersData.length; i++) {
let printer = printersData[i]
let printer = printersData[i];
printersObj[printer.name] = {
"name": printer.name,
"uri": printer.uri || "",
"state": printer.state,
"stateReason": printer.stateReason,
"location": printer.location || "",
"info": printer.info || "",
"makeModel": printer.makeModel || "",
"accepting": printer.accepting !== false,
"jobs": []
};
}
}
printers = printersObj
printers = printersObj;
if (printerNames.length > 0) {
if (selectedPrinter.length > 0) {
if (!printerNames.includes(selectedPrinter)) {
selectedPrinter = printerNames[0]
selectedPrinter = printerNames[0];
}
} else {
selectedPrinter = printerNames[0]
selectedPrinter = printerNames[0];
}
}
}
function fetchAllJobs() {
for (var i = 0; i < printerNames.length; i++) {
fetchJobsForPrinter(printerNames[i])
fetchJobsForPrinter(printerNames[i]);
}
}
function fetchJobsForPrinter(printerName) {
const params = {
"printerName": printerName
}
};
DMSService.sendRequest("cups.getJobs", params, response => {
if (response.result && printers[printerName]) {
let updatedPrinters = Object.assign({}, printers)
updatedPrinters[printerName].jobs = response.result
printers = updatedPrinters
let updatedPrinters = Object.assign({}, printers);
updatedPrinters[printerName].jobs = response.result;
printers = updatedPrinters;
}
})
});
}
function getSelectedPrinter() {
return selectedPrinter
return selectedPrinter;
}
function setSelectedPrinter(printerName) {
if (printerNames.length > 0) {
if (printerNames.includes(printerName)) {
selectedPrinter = printerName
selectedPrinter = printerName;
} else {
selectedPrinter = printerNames[0]
selectedPrinter = printerNames[0];
}
}
}
function getPrintersNum() {
if (!cupsAvailable)
return 0
return 0;
return printerNames.length
return printerNames.length;
}
function getPrintersNames() {
if (!cupsAvailable)
return []
return [];
return printerNames
return printerNames;
}
function getTotalJobsNum() {
if (!cupsAvailable)
return 0
return 0;
var result = 0
var result = 0;
for (var i = 0; i < printerNames.length; i++) {
var printerName = printerNames[i]
var printerName = printerNames[i];
if (printers[printerName] && printers[printerName].jobs) {
result += printers[printerName].jobs.length
result += printers[printerName].jobs.length;
}
}
return result
return result;
}
function getCurrentPrinterState() {
if (!cupsAvailable || !selectedPrinter)
return ""
return "";
var printer = printers[selectedPrinter]
return printer.state
var printer = printers[selectedPrinter];
return printer.state;
}
function getCurrentPrinterStatePrettyShort() {
if (!cupsAvailable || !selectedPrinter)
return ""
return "";
var printer = printers[selectedPrinter]
return getPrinterStateTranslation(printer.state) + " (" + getPrinterStateReasonTranslation(printer.stateReason) + ")"
var printer = printers[selectedPrinter];
return getPrinterStateTranslation(printer.state) + " (" + getPrinterStateReasonTranslation(printer.stateReason) + ")";
}
function getCurrentPrinterStatePretty() {
if (!cupsAvailable || !selectedPrinter)
return ""
return "";
var printer = printers[selectedPrinter]
return getPrinterStateTranslation(printer.state) + " (" + I18n.tr("Reason") + ": " + getPrinterStateReasonTranslation(printer.stateReason) + ")"
var printer = printers[selectedPrinter];
return getPrinterStateTranslation(printer.state) + " (" + I18n.tr("Reason") + ": " + getPrinterStateReasonTranslation(printer.stateReason) + ")";
}
function getCurrentPrinterJobs() {
if (!cupsAvailable || !selectedPrinter)
return []
return [];
return getJobs(selectedPrinter)
return getJobs(selectedPrinter);
}
function getJobs(printerName) {
if (!cupsAvailable)
return ""
return "";
var printer = printers[printerName]
return printer.jobs
var printer = printers[printerName];
return printer.jobs;
}
function getJobsNum(printerName) {
if (!cupsAvailable)
return 0
return 0;
var printer = printers[printerName]
return printer.jobs.length
var printer = printers[printerName];
return printer.jobs.length;
}
function pausePrinter(printerName) {
if (!cupsAvailable)
return
return;
const params = {
"printerName": printerName
}
};
DMSService.sendRequest("cups.pausePrinter", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to pause printer") + " - " + response.error)
ToastService.showError(I18n.tr("Failed to pause printer") + " - " + response.error);
} else {
getState()
getState();
}
})
});
}
function resumePrinter(printerName) {
if (!cupsAvailable)
return
return;
const params = {
"printerName": printerName
}
};
DMSService.sendRequest("cups.resumePrinter", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to resume printer") + " - " + response.error)
ToastService.showError(I18n.tr("Failed to resume printer") + " - " + response.error);
} else {
getState()
getState();
}
})
});
}
function cancelJob(printerName, jobID) {
if (!cupsAvailable)
return
return;
const params = {
"printerName": printerName,
"jobID": jobID
}
};
DMSService.sendRequest("cups.cancelJob", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to cancel selected job") + " - " + response.error)
ToastService.showError(I18n.tr("Failed to cancel selected job") + " - " + response.error);
} else {
fetchJobsForPrinter(printerName)
fetchJobsForPrinter(printerName);
}
})
});
}
function purgeJobs(printerName) {
if (!cupsAvailable)
return
return;
const params = {
"printerName": printerName
}
};
DMSService.sendRequest("cups.purgeJobs", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to cancel all jobs") + " - " + response.error)
ToastService.showError(I18n.tr("Failed to cancel all jobs") + " - " + response.error);
} else {
fetchJobsForPrinter(printerName)
fetchJobsForPrinter(printerName);
}
});
}
function getDevices() {
if (!cupsAvailable)
return;
loadingDevices = true;
DMSService.sendRequest("cups.getDevices", null, response => {
loadingDevices = false;
if (response.result) {
devices = response.result;
}
});
}
function getPPDs() {
if (!cupsAvailable)
return;
loadingPPDs = true;
DMSService.sendRequest("cups.getPPDs", null, response => {
loadingPPDs = false;
if (response.result) {
ppds = response.result;
}
});
}
function getClasses() {
if (!cupsAvailable)
return;
loadingClasses = true;
DMSService.sendRequest("cups.getClasses", null, response => {
loadingClasses = false;
if (response.result) {
printerClasses = response.result;
}
});
}
function createPrinter(name, deviceURI, ppd, options) {
if (!cupsAvailable)
return;
creatingPrinter = true;
const params = {
"name": name,
"deviceURI": deviceURI,
"ppd": ppd
};
if (options) {
if (options.shared !== undefined)
params.shared = options.shared;
if (options.location)
params.location = options.location;
if (options.information)
params.information = options.information;
if (options.errorPolicy)
params.errorPolicy = options.errorPolicy;
}
DMSService.sendRequest("cups.createPrinter", params, response => {
creatingPrinter = false;
if (response.error) {
ToastService.showError(I18n.tr("Failed to create printer") + " - " + response.error);
} else {
ToastService.showInfo(I18n.tr("Printer created successfully"));
getState();
}
});
}
function deletePrinter(printerName) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName
};
DMSService.sendRequest("cups.deletePrinter", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to delete printer") + " - " + response.error);
} else {
ToastService.showInfo(I18n.tr("Printer deleted"));
if (selectedPrinter === printerName) {
selectedPrinter = "";
}
getState();
}
});
}
function acceptJobs(printerName) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName
};
DMSService.sendRequest("cups.acceptJobs", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to enable job acceptance") + " - " + response.error);
} else {
getState();
}
});
}
function rejectJobs(printerName) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName
};
DMSService.sendRequest("cups.rejectJobs", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to disable job acceptance") + " - " + response.error);
} else {
getState();
}
});
}
function setPrinterShared(printerName, shared) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName,
"shared": shared
};
DMSService.sendRequest("cups.setPrinterShared", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to update sharing") + " - " + response.error);
} else {
getState();
}
});
}
function setPrinterLocation(printerName, location) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName,
"location": location
};
DMSService.sendRequest("cups.setPrinterLocation", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to update location") + " - " + response.error);
} else {
getState();
}
});
}
function setPrinterInfo(printerName, info) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName,
"info": info
};
DMSService.sendRequest("cups.setPrinterInfo", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to update description") + " - " + response.error);
} else {
getState();
}
});
}
function printTestPage(printerName) {
if (!cupsAvailable)
return;
const params = {
"printerName": printerName
};
DMSService.sendRequest("cups.printTestPage", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to print test page") + " - " + response.error);
} else {
ToastService.showInfo(I18n.tr("Test page sent to printer"));
fetchJobsForPrinter(printerName);
}
});
}
function moveJob(jobID, destPrinter) {
if (!cupsAvailable)
return;
const params = {
"jobID": jobID,
"destPrinter": destPrinter
};
DMSService.sendRequest("cups.moveJob", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to move job") + " - " + response.error);
} else {
fetchAllJobs();
}
});
}
function restartJob(jobID) {
if (!cupsAvailable)
return;
const params = {
"jobID": jobID
};
DMSService.sendRequest("cups.restartJob", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to restart job") + " - " + response.error);
} else {
fetchAllJobs();
}
});
}
function holdJob(jobID, holdUntil) {
if (!cupsAvailable)
return;
const params = {
"jobID": jobID
};
if (holdUntil) {
params.holdUntil = holdUntil;
}
DMSService.sendRequest("cups.holdJob", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to hold job") + " - " + response.error);
} else {
fetchAllJobs();
}
});
}
function addPrinterToClass(className, printerName) {
if (!cupsAvailable)
return;
const params = {
"className": className,
"printerName": printerName
};
DMSService.sendRequest("cups.addPrinterToClass", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to add printer to class") + " - " + response.error);
} else {
getClasses();
}
});
}
function removePrinterFromClass(className, printerName) {
if (!cupsAvailable)
return;
const params = {
"className": className,
"printerName": printerName
};
DMSService.sendRequest("cups.removePrinterFromClass", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to remove printer from class") + " - " + response.error);
} else {
getClasses();
}
});
}
function deleteClass(className) {
if (!cupsAvailable)
return;
const params = {
"className": className
};
DMSService.sendRequest("cups.deleteClass", params, response => {
if (response.error) {
ToastService.showError(I18n.tr("Failed to delete class") + " - " + response.error);
} else {
getClasses();
}
});
}
function getPrinterData(printerName) {
if (!printers || !printers[printerName])
return null;
return printers[printerName];
}
function getJobStateTranslation(state) {
switch (state) {
case "pending":
return I18n.tr("Pending");
case "pending-held":
return I18n.tr("Held");
case "processing":
return I18n.tr("Processing");
case "processing-stopped":
return I18n.tr("Stopped");
case "canceled":
return I18n.tr("Canceled");
case "aborted":
return I18n.tr("Aborted");
case "completed":
return I18n.tr("Completed");
default:
return state;
}
})
}
readonly property var states: ({
@@ -348,24 +819,24 @@ Singleton {
})
function getPrinterStateTranslation(state) {
return states[state] || state
return states[state] || state;
}
function getPrinterStateReasonTranslation(reason) {
let allReasons = Object.assign({}, reasonsGeneral, reasonsSupplies, reasonsMedia, reasonsParts, reasonsErrors, reasonsService, reasonsConnectivity)
let allReasons = Object.assign({}, reasonsGeneral, reasonsSupplies, reasonsMedia, reasonsParts, reasonsErrors, reasonsService, reasonsConnectivity);
let basReason = reason
let suffix = ""
let basReason = reason;
let suffix = "";
for (let s in severitySuffixes) {
if (reason.endsWith(s)) {
basReason = reason.slice(0, -s.length)
suffix = severitySuffixes[s]
break
basReason = reason.slice(0, -s.length);
suffix = severitySuffixes[s];
break;
}
}
let translation = allReasons[basReason] || basReason
return suffix ? translation + " (" + suffix + ")" : translation
let translation = allReasons[basReason] || basReason;
return suffix ? translation + " (" + suffix + ")" : translation;
}
}

View File

@@ -1,8 +1,6 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
@@ -14,6 +12,7 @@ Singleton {
property bool dmsAvailable: false
property var capabilities: []
property int apiVersion: 0
property string cliVersion: ""
readonly property int expectedApiVersion: 1
property var availablePlugins: []
property var installedPlugins: []
@@ -57,18 +56,18 @@ Singleton {
Component.onCompleted: {
if (socketPath && socketPath.length > 0) {
detectUpdateCommand()
detectUpdateCommand();
}
}
function detectUpdateCommand() {
checkingUpdateCommand = true
checkAurHelper.running = true
checkingUpdateCommand = true;
checkAurHelper.running = true;
}
function startSocketConnection() {
if (socketPath && socketPath.length > 0) {
testProcess.running = true
testProcess.running = true;
}
}
@@ -79,26 +78,26 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
const helper = text.trim()
const helper = text.trim();
if (helper.includes("paru")) {
checkDmsPackage.helper = "paru"
checkDmsPackage.running = true
checkDmsPackage.helper = "paru";
checkDmsPackage.running = true;
} else if (helper.includes("yay")) {
checkDmsPackage.helper = "yay"
checkDmsPackage.running = true
checkDmsPackage.helper = "yay";
checkDmsPackage.running = true;
} else {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
onExited: exitCode => {
if (exitCode !== 0) {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
@@ -112,22 +111,22 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.includes("dms-shell-git")) {
updateCommand = checkDmsPackage.helper + " -S dms-shell-git"
updateCommand = checkDmsPackage.helper + " -S dms-shell-git";
} else if (text.includes("dms-shell-bin")) {
updateCommand = checkDmsPackage.helper + " -S dms-shell-bin"
updateCommand = checkDmsPackage.helper + " -S dms-shell-bin";
} else {
updateCommand = "dms update"
updateCommand = "dms update";
}
checkingUpdateCommand = false
startSocketConnection()
checkingUpdateCommand = false;
startSocketConnection();
}
}
onExited: exitCode => {
if (exitCode !== 0) {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
@@ -138,21 +137,21 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
root.dmsAvailable = true
connectSocket()
root.dmsAvailable = true;
connectSocket();
} else {
root.dmsAvailable = false
root.dmsAvailable = false;
}
}
}
function connectSocket() {
if (!dmsAvailable || isConnected || isConnecting) {
return
return;
}
isConnecting = true
requestSocket.connected = true
isConnecting = true;
requestSocket.connected = true;
}
DankSocket {
@@ -162,32 +161,32 @@ Singleton {
onConnectionStateChanged: {
if (connected) {
root.isConnected = true
root.isConnecting = false
root.connectionStateChanged()
subscribeSocket.connected = true
root.isConnected = true;
root.isConnecting = false;
root.connectionStateChanged();
subscribeSocket.connected = true;
} else {
root.isConnected = false
root.isConnecting = false
root.apiVersion = 0
root.capabilities = []
root.connectionStateChanged()
root.isConnected = false;
root.isConnecting = false;
root.apiVersion = 0;
root.capabilities = [];
root.connectionStateChanged();
}
}
parser: SplitParser {
onRead: line => {
if (!line || line.length === 0) {
return
return;
}
console.log("DMSService: Request socket <<", line)
console.log("DMSService: Request socket <<", line);
try {
const response = JSON.parse(line)
handleResponse(response)
const response = JSON.parse(line);
handleResponse(response);
} catch (e) {
console.warn("DMSService: Failed to parse request response:", line, e)
console.warn("DMSService: Failed to parse request response:", line, e);
}
}
}
@@ -199,25 +198,25 @@ Singleton {
connected: false
onConnectionStateChanged: {
root.subscribeConnected = connected
root.subscribeConnected = connected;
if (connected) {
sendSubscribeRequest()
sendSubscribeRequest();
}
}
parser: SplitParser {
onRead: line => {
if (!line || line.length === 0) {
return
return;
}
console.log("DMSService: Subscribe socket <<", line)
console.log("DMSService: Subscribe socket <<", line);
try {
const response = JSON.parse(line)
handleSubscriptionEvent(response)
const response = JSON.parse(line);
handleSubscriptionEvent(response);
} catch (e) {
console.warn("DMSService: Failed to parse subscription event:", line, e)
console.warn("DMSService: Failed to parse subscription event:", line, e);
}
}
}
@@ -226,229 +225,227 @@ Singleton {
function sendSubscribeRequest() {
const request = {
"method": "subscribe"
}
};
if (activeSubscriptions.length > 0) {
request.params = {
"services": activeSubscriptions
}
console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions))
};
console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions));
} else {
console.log("DMSService: Subscribing to all services")
console.log("DMSService: Subscribing to all services");
}
subscribeSocket.send(request)
subscribeSocket.send(request);
}
function subscribe(services) {
if (!Array.isArray(services)) {
services = [services]
services = [services];
}
activeSubscriptions = services
activeSubscriptions = services;
if (subscribeConnected) {
subscribeSocket.connected = false
subscribeSocket.connected = false;
Qt.callLater(() => {
subscribeSocket.connected = true
})
subscribeSocket.connected = true;
});
}
}
function addSubscription(service) {
if (activeSubscriptions.includes("all")) {
console.warn("DMSService: Cannot add specific subscription when subscribed to 'all'")
return
}
if (activeSubscriptions.includes("all"))
return;
if (!activeSubscriptions.includes(service)) {
const newSubs = [...activeSubscriptions, service]
subscribe(newSubs)
const newSubs = [...activeSubscriptions, service];
subscribe(newSubs);
}
}
function removeSubscription(service) {
if (activeSubscriptions.includes("all")) {
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "dwl", "brightness", "extworkspace"]
const filtered = allServices.filter(s => s !== service)
subscribe(filtered)
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "dwl", "brightness", "extworkspace"];
const filtered = allServices.filter(s => s !== service);
subscribe(filtered);
} else {
const filtered = activeSubscriptions.filter(s => s !== service)
const filtered = activeSubscriptions.filter(s => s !== service);
if (filtered.length === 0) {
console.warn("DMSService: Cannot remove last subscription")
return
console.warn("DMSService: Cannot remove last subscription");
return;
}
subscribe(filtered)
subscribe(filtered);
}
}
function subscribeAll() {
subscribe(["all"])
subscribe(["all"]);
}
function subscribeAllExcept(excludeServices) {
if (!Array.isArray(excludeServices)) {
excludeServices = [excludeServices]
excludeServices = [excludeServices];
}
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "cups", "dwl", "brightness", "extworkspace"]
const filtered = allServices.filter(s => !excludeServices.includes(s))
subscribe(filtered)
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "cups", "dwl", "brightness", "extworkspace"];
const filtered = allServices.filter(s => !excludeServices.includes(s));
subscribe(filtered);
}
function handleSubscriptionEvent(response) {
if (response.error) {
if (response.error.includes("unknown method") && response.error.includes("subscribe")) {
if (!shownOutdatedError) {
console.error("DMSService: Server does not support subscribe method")
ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand)
shownOutdatedError = true
console.error("DMSService: Server does not support subscribe method");
ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand);
shownOutdatedError = true;
}
}
return
return;
}
if (!response.result) {
return
return;
}
const service = response.result.service
const data = response.result.data
const service = response.result.service;
const data = response.result.data;
if (service === "server") {
apiVersion = data.apiVersion || 0
capabilities = data.capabilities || []
apiVersion = data.apiVersion || 0;
cliVersion = data.cliVersion || "";
capabilities = data.capabilities || [];
console.info("DMSService: Connected (API v" + apiVersion + ") -", JSON.stringify(capabilities))
console.info("DMSService: Connected (API v" + apiVersion + ", CLI " + cliVersion + ") -", JSON.stringify(capabilities));
if (apiVersion < expectedApiVersion) {
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")")
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")");
}
capabilitiesReceived()
capabilitiesReceived();
} else if (service === "network") {
networkStateUpdate(data)
networkStateUpdate(data);
} else if (service === "network.credentials") {
credentialsRequest(data)
credentialsRequest(data);
} else if (service === "loginctl") {
if (data.event) {
loginctlEvent(data)
loginctlEvent(data);
} else {
loginctlStateUpdate(data)
loginctlStateUpdate(data);
}
} else if (service === "bluetooth.pairing") {
bluetoothPairingRequest(data)
bluetoothPairingRequest(data);
} else if (service === "cups") {
cupsStateUpdate(data)
cupsStateUpdate(data);
} else if (service === "dwl") {
dwlStateUpdate(data)
dwlStateUpdate(data);
} else if (service === "brightness") {
brightnessStateUpdate(data)
brightnessStateUpdate(data);
} else if (service === "brightness.update") {
if (data.device) {
brightnessDeviceUpdate(data.device)
brightnessDeviceUpdate(data.device);
}
} else if (service === "extworkspace") {
extWorkspaceStateUpdate(data)
extWorkspaceStateUpdate(data);
} else if (service === "wlroutput") {
wlrOutputStateUpdate(data)
wlrOutputStateUpdate(data);
} else if (service === "evdev") {
if (data.capsLock !== undefined) {
capsLockState = data.capsLock
capsLockState = data.capsLock;
}
evdevStateUpdate(data)
evdevStateUpdate(data);
}
}
function sendRequest(method, params, callback) {
if (!isConnected) {
console.warn("DMSService.sendRequest: Not connected, method:", method)
console.warn("DMSService.sendRequest: Not connected, method:", method);
if (callback) {
callback({
"error": "not connected to DMS socket"
})
});
}
return
return;
}
requestIdCounter++
const id = Date.now() + requestIdCounter
requestIdCounter++;
const id = Date.now() + requestIdCounter;
const request = {
"id": id,
"method": method
}
};
if (params) {
request.params = params
request.params = params;
}
if (callback) {
pendingRequests[id] = callback
pendingRequests[id] = callback;
}
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method)
requestSocket.send(request)
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method);
requestSocket.send(request);
}
function handleResponse(response) {
const callback = pendingRequests[response.id]
const callback = pendingRequests[response.id];
if (callback) {
delete pendingRequests[response.id]
callback(response)
delete pendingRequests[response.id];
callback(response);
}
}
function ping(callback) {
sendRequest("ping", null, callback)
sendRequest("ping", null, callback);
}
function listPlugins(callback) {
sendRequest("plugins.list", null, response => {
if (response.result) {
availablePlugins = response.result
pluginsListReceived(response.result)
availablePlugins = response.result;
pluginsListReceived(response.result);
}
if (callback) {
callback(response)
callback(response);
}
})
});
}
function listInstalled(callback) {
sendRequest("plugins.listInstalled", null, response => {
if (response.result) {
installedPlugins = response.result
installedPluginsReceived(response.result)
installedPlugins = response.result;
installedPluginsReceived(response.result);
}
if (callback) {
callback(response)
callback(response);
}
})
});
}
function search(query, category, compositor, capability, callback) {
const params = {
"query": query
}
};
if (category) {
params.category = category
params.category = category;
}
if (compositor) {
params.compositor = compositor
params.compositor = compositor;
}
if (capability) {
params.capability = capability
params.capability = capability;
}
sendRequest("plugins.search", params, response => {
if (response.result) {
searchResultsReceived(response.result)
searchResultsReceived(response.result);
}
if (callback) {
callback(response)
callback(response);
}
})
});
}
function install(pluginName, callback) {
@@ -456,12 +453,12 @@ Singleton {
"name": pluginName
}, response => {
if (callback) {
callback(response)
callback(response);
}
if (!response.error) {
listInstalled()
listInstalled();
}
})
});
}
function uninstall(pluginName, callback) {
@@ -469,12 +466,12 @@ Singleton {
"name": pluginName
}, response => {
if (callback) {
callback(response)
callback(response);
}
if (!response.error) {
listInstalled()
listInstalled();
}
})
});
}
function update(pluginName, callback) {
@@ -482,50 +479,50 @@ Singleton {
"name": pluginName
}, response => {
if (callback) {
callback(response)
callback(response);
}
if (!response.error) {
listInstalled()
listInstalled();
}
})
});
}
function lockSession(callback) {
sendRequest("loginctl.lock", null, callback)
sendRequest("loginctl.lock", null, callback);
}
function unlockSession(callback) {
sendRequest("loginctl.unlock", null, callback)
sendRequest("loginctl.unlock", null, callback);
}
function bluetoothPair(devicePath, callback) {
sendRequest("bluetooth.pair", {
"device": devicePath
}, callback)
}, callback);
}
function bluetoothConnect(devicePath, callback) {
sendRequest("bluetooth.connect", {
"device": devicePath
}, callback)
}, callback);
}
function bluetoothDisconnect(devicePath, callback) {
sendRequest("bluetooth.disconnect", {
"device": devicePath
}, callback)
}, callback);
}
function bluetoothRemove(devicePath, callback) {
sendRequest("bluetooth.remove", {
"device": devicePath
}, callback)
}, callback);
}
function bluetoothTrust(devicePath, callback) {
sendRequest("bluetooth.trust", {
"device": devicePath
}, callback)
}, callback);
}
function bluetoothSubmitPairing(token, secrets, accept, callback) {
@@ -533,12 +530,12 @@ Singleton {
"token": token,
"secrets": secrets,
"accept": accept
}, callback)
}, callback);
}
function bluetoothCancelPairing(token, callback) {
sendRequest("bluetooth.pairing.cancel", {
"token": token
}, callback)
}, callback);
}
}

View File

@@ -15,6 +15,17 @@ Item {
property var options: []
property var optionIcons: []
property bool enableFuzzySearch: false
onOptionsChanged: {
if (dropdownMenu.visible) {
dropdownMenu.fzfFinder = new Fzf.Finder(options, {
"selector": option => option,
"limit": 50,
"casing": "case-insensitive"
});
dropdownMenu.updateFilteredOptions();
}
}
property int popupWidthOffset: 0
property int maxPopupHeight: 400
property bool openUpwards: false
@@ -30,9 +41,9 @@ Item {
implicitHeight: compactMode ? 40 : Math.max(60, labelColumn.implicitHeight + Theme.spacingM)
Component.onDestruction: {
const popup = dropdownMenu
const popup = dropdownMenu;
if (popup && popup.visible) {
popup.close()
popup.close();
}
}
@@ -85,38 +96,38 @@ Item {
cursorShape: Qt.PointingHandCursor
onClicked: {
if (dropdownMenu.visible) {
dropdownMenu.close()
return
dropdownMenu.close();
return;
}
dropdownMenu.searchQuery = ""
dropdownMenu.updateFilteredOptions()
dropdownMenu.searchQuery = "";
dropdownMenu.updateFilteredOptions();
dropdownMenu.open()
dropdownMenu.open();
const pos = dropdown.mapToItem(Overlay.overlay, 0, 0)
const popupWidth = dropdownMenu.width
const popupHeight = dropdownMenu.height
const overlayHeight = Overlay.overlay.height
const pos = dropdown.mapToItem(Overlay.overlay, 0, 0);
const popupWidth = dropdownMenu.width;
const popupHeight = dropdownMenu.height;
const overlayHeight = Overlay.overlay.height;
if (root.openUpwards || pos.y + dropdown.height + popupHeight + 4 > overlayHeight) {
if (root.alignPopupRight) {
dropdownMenu.x = pos.x + dropdown.width - popupWidth
dropdownMenu.x = pos.x + dropdown.width - popupWidth;
} else {
dropdownMenu.x = pos.x - (root.popupWidthOffset / 2)
dropdownMenu.x = pos.x - (root.popupWidthOffset / 2);
}
dropdownMenu.y = pos.y - popupHeight - 4
dropdownMenu.y = pos.y - popupHeight - 4;
} else {
if (root.alignPopupRight) {
dropdownMenu.x = pos.x + dropdown.width - popupWidth
dropdownMenu.x = pos.x + dropdown.width - popupWidth;
} else {
dropdownMenu.x = pos.x - (root.popupWidthOffset / 2)
dropdownMenu.x = pos.x - (root.popupWidthOffset / 2);
}
dropdownMenu.y = pos.y + dropdown.height + 4
dropdownMenu.y = pos.y + dropdown.height + 4;
}
if (root.enableFuzzySearch && searchField.visible) {
searchField.forceActiveFocus()
searchField.forceActiveFocus();
}
}
}
@@ -133,8 +144,8 @@ Item {
DankIcon {
name: {
const currentIndex = root.options.indexOf(root.currentValue)
return currentIndex >= 0 && root.optionIcons.length > currentIndex ? root.optionIcons[currentIndex] : ""
const currentIndex = root.options.indexOf(root.currentValue);
return currentIndex >= 0 && root.optionIcons.length > currentIndex ? root.optionIcons[currentIndex] : "";
}
size: 18
color: Theme.surfaceText
@@ -186,39 +197,39 @@ Item {
function updateFilteredOptions() {
if (!root.enableFuzzySearch || searchQuery.length === 0) {
filteredOptions = root.options
selectedIndex = -1
return
filteredOptions = root.options;
selectedIndex = -1;
return;
}
const results = fzfFinder.find(searchQuery)
filteredOptions = results.map(result => result.item)
selectedIndex = -1
const results = fzfFinder.find(searchQuery);
filteredOptions = results.map(result => result.item);
selectedIndex = -1;
}
function selectNext() {
if (filteredOptions.length === 0) {
return
return;
}
selectedIndex = (selectedIndex + 1) % filteredOptions.length
listView.positionViewAtIndex(selectedIndex, ListView.Contain)
selectedIndex = (selectedIndex + 1) % filteredOptions.length;
listView.positionViewAtIndex(selectedIndex, ListView.Contain);
}
function selectPrevious() {
if (filteredOptions.length === 0) {
return
return;
}
selectedIndex = selectedIndex <= 0 ? filteredOptions.length - 1 : selectedIndex - 1
listView.positionViewAtIndex(selectedIndex, ListView.Contain)
selectedIndex = selectedIndex <= 0 ? filteredOptions.length - 1 : selectedIndex - 1;
listView.positionViewAtIndex(selectedIndex, ListView.Contain);
}
function selectCurrent() {
if (selectedIndex < 0 || selectedIndex >= filteredOptions.length) {
return
return;
}
root.currentValue = filteredOptions[selectedIndex]
root.valueChanged(filteredOptions[selectedIndex])
close()
root.currentValue = filteredOptions[selectedIndex];
root.valueChanged(filteredOptions[selectedIndex]);
close();
}
parent: Overlay.overlay
@@ -270,8 +281,8 @@ Item {
topPadding: Theme.spacingS
bottomPadding: Theme.spacingS
onTextChanged: {
dropdownMenu.searchQuery = text
dropdownMenu.updateFilteredOptions()
dropdownMenu.searchQuery = text;
dropdownMenu.updateFilteredOptions();
}
Keys.onDownPressed: dropdownMenu.selectNext()
Keys.onUpPressed: dropdownMenu.selectPrevious()
@@ -279,17 +290,17 @@ Item {
Keys.onEnterPressed: dropdownMenu.selectCurrent()
Keys.onPressed: event => {
if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
dropdownMenu.selectNext()
event.accepted = true
dropdownMenu.selectNext();
event.accepted = true;
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
dropdownMenu.selectPrevious()
event.accepted = true
dropdownMenu.selectPrevious();
event.accepted = true;
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
dropdownMenu.selectNext()
event.accepted = true
dropdownMenu.selectNext();
event.accepted = true;
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
dropdownMenu.selectPrevious()
event.accepted = true
dropdownMenu.selectPrevious();
event.accepted = true;
}
}
}
@@ -362,9 +373,9 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.currentValue = modelData
root.valueChanged(modelData)
dropdownMenu.close()
root.currentValue = modelData;
root.valueChanged(modelData);
dropdownMenu.close();
}
}
}