ambrevar: Move magic mime to a file+mime class and methods.

master
Pierre Neidhardt 2021-01-15 16:43:43 +01:00
parent 7ae87c82d4
commit e6e0d806d2
2 changed files with 69 additions and 48 deletions

View File

@ -77,10 +77,11 @@ This returns the directory name for directories."
stream)))
(export-always 'file)
(defun file (path)
(let ((native-path (uiop:truename* (if (pathnamep path)
path
(uiop:parse-native-namestring path)))))
(defmethod initialize-instance :after ((file file) &key)
(let* ((path (path file))
(native-path (uiop:truename* (if (pathnamep path)
path
(uiop:parse-native-namestring path)))))
(assert (or (uiop:file-exists-p native-path)
(uiop:directory-exists-p native-path)))
;; TODO: What do we do with non-existent files (e.g. unsaved emacs buffers)? Just return nil?
@ -92,18 +93,21 @@ This returns the directory name for directories."
(loop for (name . value) in osicat::+permissions+
when (plusp (logand mode value))
collect name))))
(make-instance 'file
:path (uiop:unix-namestring native-path)
:inode (osicat-posix:stat-ino stat)
:link-count (osicat-posix:stat-nlink stat)
:kind (osicat:file-kind native-path) ; TODO: Don't recall `stat'.
:size (osicat-posix:stat-size stat)
:user-id (osicat-posix:stat-uid stat)
:group-id (osicat-posix:stat-gid stat)
:creation-date (local-time:unix-to-timestamp (osicat-posix:stat-ctime stat))
:modification-date (local-time:unix-to-timestamp (osicat-posix:stat-mtime stat))
:access-date (local-time:unix-to-timestamp (osicat-posix:stat-atime stat))
:permissions (stat-permissions stat)))))))
(setf
(path file) (uiop:unix-namestring native-path)
(inode file) (osicat-posix:stat-ino stat)
(link-count file) (osicat-posix:stat-nlink stat)
(kind file) (osicat:file-kind native-path) ; TODO: Don't recall `stat'.
(size file) (osicat-posix:stat-size stat)
(user-id file) (osicat-posix:stat-uid stat)
(group-id file) (osicat-posix:stat-gid stat)
(creation-date file) (local-time:unix-to-timestamp (osicat-posix:stat-ctime stat))
(modification-date file) (local-time:unix-to-timestamp (osicat-posix:stat-mtime stat))
(access-date file) (local-time:unix-to-timestamp (osicat-posix:stat-atime stat))
(permissions file) (stat-permissions stat)))))))
(defun file (path)
(make-instance 'file :path path))
(export-always 'list-directory)
(defun list-directory (&optional (path *default-pathname-defaults*) sort)
@ -162,3 +166,52 @@ Without PREDICATES, list all files."
"Return a file predicate that matches on modification time #'> than timestamp."
(lambda (file)
(local-time:timestamp> (modification-date file) timestamp)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defparameter %magic-cookie-mime nil
"Internal storage for `magic-cookie-mime'.")
(defun magic-cookie-mime ()
"Return internal, persistent MIME cookie for `magicffi' calls.
Benchmark on thousands of files shows that
keeping the same cookie saves about 15% of time. "
(when (or (not %magic-cookie-mime)
(not (magicffi:open-magic-p %magic-cookie-mime)))
(setf %magic-cookie-mime (magicffi:magic-open '(:symlink :mime)))
(magicffi:magic-load %magic-cookie-mime))
%magic-cookie-mime)
(defparameter %magic-cookie-description nil
"Internal storage for `magic-cookie-description'.")
(defun magic-cookie-description ()
"Same as `magic-cooke-mime' but for `file' descriptions.
See `%description'."
(when (or (not %magic-cookie-description)
(not (magicffi:open-magic-p %magic-cookie-description)))
(setf %magic-cookie-description (magicffi:magic-open '(:symlink)))
(magicffi:magic-load %magic-cookie-description))
%magic-cookie-description)
(defun %mime-type+encoding (path)
"Return a pair of MIME type and MIME encoding for PATH."
(str:split "; "
(magicffi:magic-file (magic-cookie-mime) path)))
(defun %description (path)
"Return the PATH description as per the `file' UNIX command."
(magicffi:magic-file (magic-cookie-description) path))
;; TODO: Include the description or do it in another class? Could be slower. Benchmark.
(defclass* file+mime (file)
((mime-type "")
(mime-encoding "")
(description ""))
(:accessor-name-transformer #'name-identity))
(defmethod initialize-instance :after ((file file+mime) &key)
(let ((mime-type+encoding (%mime-type+encoding (path file))))
(setf (mime-type file) (first mime-type+encoding)
(mime-encoding file) (second mime-type+encoding)
(description file) (%description (path file)))))
(defun file+mime (path)
(make-instance 'file+mime :path path))

View File

@ -25,38 +25,6 @@
(delete-if #'sera:resolve-executable (cons program more-programs))))
(error "Missing programs: ~{~a~,^, ~}" missing-programs)))
(defvar %magic-cookie-mime-type (magicffi:magic-open '(:symlink :mime-type))
"Internal, persistent cookie for `magicffi' calls.
Benchmark on thousands of files shows that
keeping the same cookie saves about 15% of time.")
(magicffi:magic-load %magic-cookie-mime-type)
(defvar %magic-cookie-mime (magicffi:magic-open '(:symlink :mime))
"See `%magic-cookie-mime-type'. ")
(magicffi:magic-load %magic-cookie-mime)
(defvar %magic-cookie-description (magicffi:magic-open '(:symlink))
"See `%magic-cookie-mime-type'. ")
(magicffi:magic-load %magic-cookie-description)
(export-always 'file-mime-type)
(defun file-mime-type (file)
"Return the FILE MIME type."
(magicffi:magic-file %magic-cookie-mime-type file))
(export-always 'file-mime)
(defun file-mime (file)
"Return a pair of MIME type and MIME encoding for FILE."
(str:split "; "
(magicffi:magic-file %magic-cookie-mime file)))
(export-always 'file-description)
(defun file-description (file)
"Return the FILE description as per the `file' UNIX command."
(magicffi:magic-file %magic-cookie-description file))
(export-always 'match-extensions)
(defun match-extensions (extension &rest more-extensions)
"Return a predicate for files that match on of the provided extensions.