Garciat
12/30/2015 - 6:42 AM

pacman packages by date

pacman packages by date

module Main where

import Data.Maybe
import Data.List
import Data.List.Split
import System.Process
import Data.Time.Parse
import Data.Time.LocalTime
import Data.Time.ISO8601

import qualified Data.Text as T

----------------

coalesce :: (a -> a -> Maybe a) -> [a] -> [a]
coalesce f x =
    case x of
      (p:q:x') ->
          case f p q of
            Nothing -> p : coalesce f (q : x')
            Just r -> coalesce f (r : x')
      _ -> x

fromRight :: Either a b -> b
fromRight (Right x) = x

strip :: String -> String
strip = T.unpack . T.strip . T.pack

----------------

type Entry = (String, String)

type Package = [Entry]

pacmanQueryInfoRaw :: IO String
pacmanQueryInfoRaw = readProcess "pacman" ["-Qi"] []

pacmanQueryInfo :: IO [Package]
pacmanQueryInfo = parsePackageList <$> pacmanQueryInfoRaw

parsePackageList :: String -> [Package]
parsePackageList input = map parsePackage blocks
  where
    blocks = endBy "\n\n" input

parsePackage :: String -> Package
parsePackage input = map fromRight $ coalesce joiner entries
  where
    inputLines = lines input
    entries    = map parsePackageEntry inputLines
    
    joiner (Right (title, value)) (Left value') = Just $ Right (title, value ++ " " ++ value')
    joiner _ _ = Nothing

parsePackageEntry :: String -> Either String Entry
parsePackageEntry input =
  case break (== ':') input of
    (value, [])        -> Left $ strip value
    (title, (_:value)) -> Right (strip title, strip value)

packageEntryValue :: String -> Package -> String
packageEntryValue key = fromJust . lookup key

packageName :: Package -> String
packageName = packageEntryValue "Name"

packageVersion :: Package -> String
packageVersion = packageEntryValue "Version"

packageInstallDate :: Package -> String
packageInstallDate = formatPackageDate . packageEntryValue "Install Date"

formatPackageDate :: String -> String
formatPackageDate input = formatISO8601 $ localTimeToUTC utc time
  where
    Just (time, _) = strptime "%a %d %b %Y %I:%M:%S %p" input

formatPackage :: Package -> String
formatPackage package = intercalate " " [installDate, name, version]
  where
    name        = packageName package
    version     = packageVersion package
    installDate = packageInstallDate package

main = do { packages <- pacmanQueryInfo
          ; putStrLn $ unlines $ map formatPackage packages
          }
import std.stdio;
import std.process;
import std.string;
import std.range;
import std.algorithm;
import std.typecons;
import core.sys.posix.time;

bool notNullOrEmpty(T)(T s)
{
  return s != null || !s.empty;
}

bool notNull(T)(T s)
{
  return s != null;
}

auto dict(alias kf, alias vf, T)(T items)
{
  alias TK = typeof(kf(items.front));
  alias TV = typeof(vf(items.front));
  
  TV[TK] result;
  
  foreach (item; items)
  {
    result[kf(item)] = vf(item);
  }
  
  return result;
}

string[string][] getPacmanPackages()
{
  struct Property
  {
    string key, value;
  }
  
  auto parseProperty(string line)
  {
    auto ix = line.indexOf(':');
    if (ix == -1) return null;
    
    auto k = line[0..ix].strip;
    auto v = line[ix+1..$].strip;
    
    return new Property(k, v);
  }
  
  auto parsePackage(string lines)
  {
    return
      lines
      .splitter('\n')
      .map!parseProperty
      .filter!notNull
      .dict!(x => x.key, x => x.value);
  }
  
  return
    ["pacman", "-Qi"]
    .execute
    .output
    .splitter("\n\n")
    .filter!notNullOrEmpty
    .map!parsePackage
    .array;
}

auto fmtDateTime(string s)
{
  tm time;
  
  strptime(s.ptr, "%a %d %b %Y %I:%M:%S %p %Z", &time);
  
  char buf[30];
  
  auto len = strftime(buf.ptr, buf.length, "%Y-%m-%dT%H:%M:%S%Z", &time);
  
  auto fmt = buf[0..len].idup;
  
  return fmt;
}

void main()
{
  auto pks = getPacmanPackages();
  
  foreach (pk; pks)
  {
    writeln(pk["Install Date"].fmtDateTime, ' ', pk["Name"], ' ', pk["Version"]);
  }
}