cripslock

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

path.fnl (4114B)


      1 (import-macros { : define-tests : expect : expect= } :assert)
      2 (local { : clone } (require :lib))
      3 
      4 (local Path
      5        (let [mt {}
      6              new (fn [components absolute? directory?]
      7                    (doto { : components : absolute? : directory? }
      8                      (setmetatable mt)))
      9              parse
     10              (fn [raw]
     11                (if (= (type raw) :string)
     12                    (new (icollect [c (string.gmatch raw "([^/]+)")] c)
     13                         (string.find raw "^/")
     14                         (or (string.find raw "/$")
     15                             (string.find raw "/%.$")
     16                             (string.find raw "/%.%.$")))
     17                    (new raw.components raw.absolute? raw.directory?)))
     18              join
     19              (fn [base leaf]
     20                            (let [addl (parse leaf)]
     21                              (if addl.absolute?
     22                                  addl
     23                                  (let [c (clone base.components)]
     24                                    (each [_ v (ipairs addl.components)]
     25                                      (table.insert c v))
     26                                    (new c base.absolute? addl.directory? )))))
     27              canonicalise
     28              (fn [self]
     29                (let [out []]
     30                  (each [_ component (ipairs self.components)]
     31                    (case component
     32                      ".." (table.remove out)
     33                      "." nil
     34                      c (table.insert out c)))
     35                  (new out self.absolute? self.directory?)))
     36              ]
     37          (tset mt :__div join)
     38          (tset mt :__index {
     39                             :parent (fn [x]
     40                                       (new (doto (clone x.components) (table.remove))
     41                                            x.absolute?))
     42                             :join join
     43                             :canonicalise canonicalise
     44                             :relativise (fn [p]
     45                                           (new p.components false p.directory?))
     46                             })
     47          (tset mt :__tostring (fn [self]
     48                                 (.. (if self.absolute? "/" "")
     49                                     (table.concat self.components "/")
     50                                     (if self.directory? "/" "")
     51                                     )))
     52 
     53 
     54      parse))
     55 
     56 (define-tests :path
     57   (let [p (Path "/etc/passwd")
     58         q (Path "/var/spool/mail/")]
     59     (expect= p.components ["etc" "passwd"])
     60     (expect p.absolute?)
     61     (expect= (tostring (/ p "foo/bar/")) "/etc/passwd/foo/bar/")
     62     (expect= (tostring (p:parent)) "/etc")
     63     (expect q.directory?)
     64     (let [c (/ p "subdir/")]
     65       (expect= (tostring c) "/etc/passwd/subdir/")
     66       (expect c.directory?))
     67     (let [c (/ p (Path "subdir/"))]
     68       (expect= (tostring c) "/etc/passwd/subdir/")
     69       (expect c.directory?))
     70 
     71     (let [p (Path "/etc/../var/log/../spool/foo")]
     72       (expect= (tostring (p:canonicalise)) "/var/spool/foo"))
     73     (let [p (Path "/etc/private/..")]
     74       (expect p.absolute?)
     75       (expect p.directory?))
     76 
     77     ))
     78 
     79 
     80 (define-tests :dedot-path
     81   (let [dedot-path #(-> "/" Path (/ $1) (: :canonicalise) tostring)]
     82     (expect= (dedot-path "/hello") "/hello")
     83     ;; relative pathnames are treated as if at root directory
     84     (expect= (dedot-path "hello") "/hello")
     85     (expect= (dedot-path "hello/goo") "/hello/goo")
     86     ;; repeated / is treated as if single /
     87     (expect= (dedot-path "/hello///goodbye") "/hello/goodbye")
     88     (expect= (dedot-path "/hello/there/../goodbye") "/hello/goodbye")
     89     ;; if the input ends in / then preserve it
     90     (expect= (dedot-path "/hello/there/") "/hello/there/")
     91     ;; if the final entry is . or .. then we must be desiring a directory
     92     (expect= (dedot-path "/hello/there/..") "/hello/")
     93     (expect= (dedot-path "/hello/there/../.") "/hello/")
     94 
     95     (expect= (dedot-path "/./../hello/") "/hello/")
     96 
     97     (expect= (dedot-path "/hello/there/ab") "/hello/there/ab")
     98     (expect= (dedot-path "/hello/there/./goodbye") "/hello/there/goodbye")
     99 
    100     (expect= (dedot-path "../../foo") "/foo")
    101     ))
    102 
    103 
    104 
    105 
    106 { : Path }