Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Learning Clojure - Package Manager
(ns package_manager
(:use [clojure.string :only [split, lower-case]]))
(def system (atom {}))
(def installed (atom (sorted-set)))
(def running (atom true))
(defn in? [coll elm]
(some #{elm} coll))
(defn package
"makes a package element with provided name, option list of providers
are packages requried by package, optional list of clients are packages using
(package name #{} #{}))
([name providers clients]
{:name name, :providers providers, :clients clients}))
(defn add-client [pkg client]
(package (:name pkg) (:providers pkg) (conj (:clients pkg) client)))
(defn remove-client [pkg client]
(package (:name pkg) (:providers pkg) (disj (:clients pkg) client)))
(defn add-provider
([pkg] pkg)
([pkg provider]
(package (:name pkg) (conj (:providers pkg) provider) (:clients pkg)))
([pkg provider & more-providers]
(reduce add-provider pkg (cons provider more-providers))))
(defn providers [pkg]
(get-in @system [pkg :providers ]))
(defn clients [pkg]
(get-in @system [pkg :clients ]))
(defn retrieve [pkg]
(get @system pkg))
(defn exists? [pkg]
(contains? @system pkg))
(defn dependent? [first second]
(if (in? (cons first (providers first)) second)
(do (println (str "\t" first) "depends on" second) true)
(some #(dependent? % second) (providers first))))
(defn update! [pkg]
(swap! system assoc (:name pkg) pkg))
(defn create! [pkg & deps]
(doseq [dep deps]
(if-not (exists? dep) (update! (package dep))))
(if (not-any? #(dependent? % pkg) deps)
(update! (apply add-provider (cons (package pkg) deps)))
(println "Ignoring command")))
(defn print-sys []
(doseq [[k,v] @system] (println "\t" v)))
(defn print-installed []
(doseq [v @installed] (println "\t" v)))
(defn installed? [pkg]
(contains? @installed pkg))
(defn sys-install! [pkg]
(println "\t installing" pkg)
(swap! installed conj pkg))
(defn install! [pkg & {:keys [is-dep]}]
(if-not (exists? pkg) (create! pkg))
(if-not (installed? pkg)
(doseq [provider (providers pkg)]
(if-not (installed? provider) (install! provider :is-dep true)))
(doseq [provider (providers pkg)]
(update! (add-client (retrieve provider) pkg)))
(if-not is-dep (update! (add-client (retrieve pkg) pkg)))
(sys-install! pkg))
(if-not is-dep (update! (add-client (retrieve pkg) pkg)))
(println "\t" pkg "is already installed."))))
(defn needed? [pkg & {:keys [is-dep]}]
(let [pkg-clients (if is-dep
(clients pkg)
(disj (clients pkg) pkg))]
(not (empty? pkg-clients))))
(defn sys-uninstall! [pkg]
(println "\t uninstalling" pkg)
(swap! installed disj pkg))
(defn uninstall! [pkg & {:keys [is-dep]}]
(not (installed? pkg))
(println "\t" pkg "is not installed.")
(needed? pkg :is-dep is-dep)
(println "\t" pkg "is still needed.")
:else (do
(doseq [provider (providers pkg)]
(update! (remove-client (retrieve provider) pkg)))
(update! (remove-client (retrieve pkg) pkg))
(sys-uninstall! pkg)
(doseq [provider (filter #(not (needed? % :is-dep true))
(providers pkg))]
(uninstall! provider :is-dep true)))))
(defn stop! []
(reset! running false))
(defn exit []
(println "goodbye") (stop!))
(defn run []
(println "clojure package manager")
(reset! running true)
(while (true? @running)
(let [line (read-line)
[command & args] (split line #"\s+")]
(case (-> command lower-case keyword)
:depend (apply create! args)
:list (print-installed)
:install (doseq [pkg args] (install! pkg))
:info (println (retrieve (first args)))
(:remove :uninstall ) (doseq [pkg args] (uninstall! pkg))
:sys (print-sys)
(:exit :end ) (exit)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment