packages feed

ls-usb-0.1.0.5: ls-usb.hs

{-# LANGUAGE DeriveDataTypeable, NoImplicitPrelude, UnicodeSyntax #-}

module Main where

-- ansi-wl-pprint
import Text.PrettyPrint.ANSI.Leijen   ( putDoc, plain )

-- base
import Control.Monad                  ( (>>=), (>>), (=<<), fail )
import Data.Bool                      ( Bool(False, True), otherwise )
import Data.Data                      ( Data )
import Data.Function                  ( ($), const, id )
import Data.Int                       ( Int )
import Data.List                      ( filter, foldr, map )
import Data.Typeable                  ( Typeable )
import Data.Word                      ( Word8 )
import Prelude                        ( fromIntegral )
import System.IO                      ( IO, putStrLn )
import Text.Show                      ( Show )

-- base-unicode-symbols
import Data.Function.Unicode          ( (∘) )
import Data.Bool.Unicode              ( (∧), (∨) )
import Data.Eq.Unicode                ( (≡) )

-- CmdArgs
import System.Console.CmdArgs         ( Mode
                                      , (&), (&=)
                                      , mode, def, explicit, flag, typ, text
                                      , prog, helpSuffix, cmdArgs, isLoud
                                      )

-- ls-usb (this program)
import PrettyDevList                  ( ppDevices
                                      , brightStyle, darkStyle
                                      )

-- usb
import System.USB.Initialization      ( newCtx )
import System.USB.Enumeration         ( Device
                                      , getDevices, deviceDesc
                                      , busNumber, deviceAddress
                                      )
import System.USB.Descriptors         ( VendorId, ProductId
                                      , deviceVendorId, deviceProductId
                                      )

-- usb-id-database
import System.USB.IDDB.LinuxUsbIdRepo ( staticDb )


-------------------------------------------------------------------------------
-- Main

data Options = Options { vid      ∷ [Int]
                       , pid      ∷ [Int]
                       , bus      ∷ [Int]
                       , address  ∷ [Int]
                       , nocolour ∷ Bool
                       , darker   ∷ Bool
                       } deriving (Show, Data, Typeable)

defaultOpts ∷ Mode Options
defaultOpts = mode Options
  { vid      = def &= explicit & flag "vid" & typ "VID"
             & text "List devices with this VID"
  , pid      = def &= explicit & flag "pid" & typ "PID"
             & text "List devices with this PID"
  , bus      = def &= explicit & flag "b" & flag "bus" & typ "BUS"
             & text "List devices on this BUS"
  , address  = def &= explicit & flag "a" & flag "address" & typ "ADDR"
             & text "List devices with this ADDRESS"
  , nocolour = def &= explicit & flag "nc" & flag "nocolour" & flag "nocolor"
             & text "Don't colour the output"
  , darker   = def &= explicit & flag "dark"
             & text "Use darker colours (for bright backgrounds)"
  } &= prog "ls-usb"
    & text "Lists connected USB devices"
    & helpSuffix ["Please ensure you have sufficient rights before running with higher verbosity"]

main ∷ IO ()
main = do opts    ← cmdArgs "ls-usb 0.1.0.5, (C) Roel van Dijk 2009-2010"
                            [defaultOpts]
          verbose ← isLoud
          db      ← staticDb
          ctx     ← newCtx
          let style | darker opts = darkStyle
                    | otherwise   = brightStyle
          (putDoc ∘ if nocolour opts then plain else id)
              =<< ppDevices style db verbose
              ∘   filter (filterFromOpts opts)
              =<< getDevices ctx
          putStrLn ""

filterFromOpts ∷ Options → F Device
filterFromOpts opts = andF $ map (filterNonEmpty ∘ ($ opts))
                      [ map (matchVID     ∘ fromIntegral) ∘ vid
                      , map (matchPID     ∘ fromIntegral) ∘ pid
                      , map (matchBus     ∘ fromIntegral) ∘ bus
                      , map (matchDevAddr ∘ fromIntegral) ∘ address
                      ]


-------------------------------------------------------------------------------
-- Filters

type F α = α → Bool

-- Construct a filter combinator from a binary boolean operator.
binBoolOpToFComb ∷ (Bool → Bool → Bool) → F α → F α → F α
binBoolOpToFComb (⊗) f g = \x → f x ⊗ g x

(<∨>) ∷ F α → F α → F α
(<∨>) = binBoolOpToFComb (∨)

(<∧>) ∷ F α → F α → F α
(<∧>) = binBoolOpToFComb (∧)

andF ∷ [F α] → F α
andF = foldr (<∧>) (const True)

orF ∷ [F α] → F α
orF = foldr (<∨>) (const False)

filterNonEmpty ∷ [F α] → F α
filterNonEmpty [] = const True
filterNonEmpty xs = foldr (<∨>) (const False) xs

-------------------------------------------------------------------------------
-- Specific Device filters

matchVID ∷ VendorId → F Device
matchVID vid' = (vid' ≡) ∘ deviceVendorId ∘ deviceDesc

matchPID ∷ ProductId → F Device
matchPID pid' = (pid' ≡) ∘ deviceProductId ∘ deviceDesc

matchBus ∷ Word8 → F Device
matchBus bus' = (bus' ≡) ∘ busNumber

matchDevAddr ∷ Word8 → F Device
matchDevAddr addr = (addr ≡) ∘ deviceAddress