In a Turtle script for getting passwords from a keyring, calling ssh-add with those passwords so they don't have to be filled out manually, is the following function:
processKey :: (T.Text, T.Text) -> Shell Line -> IO ()
processKey (kn, str) pwds = do
-- Not sure why (text str) is needed here, but it won't typecheck without
-- it (despite OverloadedStrings).
let expectArg = do
ml <- grep (contains (text str)) pwds
let pass = getPwd $ cut tab (format l ml)
return $ T.unlines [ "<< EOF"
, "spawn ssh-add"
, "expect \"Enter passphrase\""
, "send " <> pass
, "expect eof"
, "EOF"
]
view (inproc "expect" [] expectArg)
where
-- Safely get the third item `cut` from the list.
getPwd xs = getPwd' 0 xs
getPwd' _ [] = ""
getPwd' n (x:xs) = if n == 2
then x
else getPwd' (n+1) xs
This function takes a tuple of (SSH key file name, string to search for in the text stored in the keyring), and pwds :: Shell Line that is the entire contents of the keyring taken from a shell command.
The purpose of the function is to grep the passwords, and call ssh-add with the key filename and password.
The problem is this function doesn't type check:
sshkeys-autopass.hs:45:30: error:
• Couldn't match type ‘Text’ with ‘Line’
Expected type: Shell Line
Actual type: Shell Text
• In the third argument of ‘inproc’, namely ‘expectArg’
In the first argument of ‘view’, namely
‘(inproc "expect" [] expectArg)’
In a stmt of a 'do' block: view (inproc "expect" [] expectArg)
|
45 | view (inproc "expect" [] expectArg)
| ^^^^^^^^^
It seems like Shell Line needs to become Shell Text, how can this be done, please? I am open to the possibility this is structured badly or is not idiomatic Haskell (it does smell), if so please advise how this function could be better.
While I can't try out your code right now, it seems roundtripping your commands through
Text(asT.unlinesforces you to) is causing unnecessary trouble. According to the documentation (emphasis mine):As a
Shell Lineis a stream, it can supply multipleLines. And sure enough, there is a function calledselect...... which will convert a list (or any other
Foldable) to aShell. You can use it to get theShell Lineyou need directly:Side questions:
The only thing
OverloadedStringsdoes automatically is handling string literals. It won't silently convertTextvalues to other instances ofIsString. An alternative to usingtextwould be changing your signature so that the type ofstrisPattern Textrather thanText.Here is one way of writing
getPwdwithout writing the recursive algorithm explicitly, using a few functions fromData.Maybe:You might also like the
atDeffrom the safe package:NonEmptyis a type for lists that are guaranteed to have at least one element. In your case, the lack of a sensible way of going fromNonEmpty LinetoLine(concatenating the elements or picking the first one, for instance, wouldn't help at all) was a signal that a change of approach was necessary.