aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/dev/msfjarvis/aps/util/extensions/Extensions.kt
blob: 6e14a29d7ee8632b2c909cf44243e64c03c6593a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
 * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
 * SPDX-License-Identifier: GPL-3.0-only
 */
package dev.msfjarvis.aps.util.extensions

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.runCatching
import dev.msfjarvis.aps.data.repo.PasswordRepository
import java.io.File
import java.util.Date
import logcat.asLog
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevCommit

/** The default OpenPGP provider for the app */
const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"

/** Clears the given [flag] from the value of this [Int] */
fun Int.clearFlag(flag: Int): Int {
  return this and flag.inv()
}

/** Checks if this [Int] contains the given [flag] */
infix fun Int.hasFlag(flag: Int): Boolean {
  return this and flag == flag
}

/** Checks whether this [File] is a directory that contains [other]. */
fun File.contains(other: File): Boolean {
  if (!isDirectory) return false
  if (!other.exists()) return false
  val relativePath =
    runCatching { other.relativeTo(this) }.getOrElse {
      return false
    }
  // Direct containment is equivalent to the relative path being equal to the filename.
  return relativePath.path == other.name
}

/**
 * Checks if this [File] is in the password repository directory as given by
 * [PasswordRepository.getRepositoryDirectory]
 */
fun File.isInsideRepository(): Boolean {
  return canonicalPath.contains(PasswordRepository.getRepositoryDirectory().canonicalPath)
}

/** Recursively lists the files in this [File], skipping any directories it encounters. */
fun File.listFilesRecursively() = walkTopDown().filter { !it.isDirectory }.toList()

/**
 * Unique SHA-1 hash of this commit as hexadecimal string.
 *
 * @see RevCommit.getId
 */
val RevCommit.hash: String
  get() = ObjectId.toString(id)

/**
 * Time this commit was made with second precision.
 *
 * @see RevCommit.commitTime
 */
val RevCommit.time: Date
  get() {
    val epochSeconds = commitTime.toLong()
    val epochMilliseconds = epochSeconds * 1000
    return Date(epochMilliseconds)
  }

/**
 * Splits this [String] into an [Array] of [String] s, split on the UNIX LF line ending and stripped
 * of any empty lines.
 */
fun String.splitLines(): Array<String> {
  return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
}

/** Alias to [lazy] with thread safety mode always set to [LazyThreadSafetyMode.NONE]. */
fun <T> unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE) { initializer.invoke() }

/** A convenience extension to turn a [Throwable] with a message into a loggable string. */
fun Throwable.asLog(message: String): String = "$message\n${asLog()}"

/** Extension on [Result] that returns if the type is [Ok] */
fun <V, E> Result<V, E>.isOk(): Boolean {
  return this is Ok<V>
}

/** Extension on [Result] that returns if the type is [Err] */
fun <V, E> Result<V, E>.isErr(): Boolean {
  return this is Err<E>
}