Current section

Files

Jump to
novdom_dev_tools src lustre_dev_tools utils.gleam
Raw

src/lustre_dev_tools/utils.gleam

import gleam/list
import gleam/result
import gleam/string
import term_size
pub fn term_width() -> Int {
term_size.columns()
|> result.unwrap(80)
}
pub fn shorten_url(url: String, to max_length: Int) -> String {
let chunks = case url {
"https://" <> rest -> ["https:/", ..string.split(rest, on: "/")]
_ -> string.split(url, on: "/")
}
// We want to reduce the max length further to offset the `/` we'll
// be adding back to join the remaining pieces.
let max_length = max_length - list.length(chunks)
case shorten(chunks, to: max_length) {
Error(_) -> url
Ok(#(left, right)) ->
string.join(left, "/") <> "/.../" <> string.join(right, "/")
}
}
/// Shortens a list of strings by removing pieces from the middle until it gets
/// down to the desired length, returning its two remaining halves.
/// Returns an error if it couldn't shorten the list.
///
fn shorten(
strings: List(String),
to max_length: Int,
) -> Result(#(List(String), List(String)), Nil) {
let initial_length =
list.fold(over: strings, from: 0, with: fn(acc, string) {
acc + string.length(string)
})
// We want to divide the strings in two halves and remove items starting from
// the middle going in both directions until we get to the desired length or
// we can shorten it any further.
let middle = list.length(strings) / 2
let #(left, right) = list.split(strings, middle)
// It's important we reverse the left part because we want to remove pieces
// from its end, that is the part nearer to the middle of `strings`.
let left = list.reverse(left)
case do_shorten(left, right, False, Right, initial_length, max_length) {
// Remember that the left part was reversed so that `do_shorten` could
// remove items from its start! We have to reverse it back to normal.
Ok(#(new_left, new_right)) -> Ok(#(list.reverse(new_left), new_right))
Error(Nil) -> Error(Nil)
}
}
type End {
Left
Right
}
/// Drops strings from the start of each list in turns until it shrinks it down
/// to the desired size.
/// If it could acutally shorten the lists it returns `Ok` wrapping them,
/// otherwise it returns `Error(Nil)`.
///
fn do_shorten(
left: List(String),
right: List(String),
shortened: Bool,
from: End,
current_length: Int,
max_length: Int,
) -> Result(#(List(String), List(String)), Nil) {
case current_length <= max_length, left, right, from {
// If we're already shorter than the maximum allowed length we don't shrink
// the lists any further.
True, _, _, _ ->
case shortened {
True -> Ok(#(left, right))
False -> Error(Nil)
}
// If we're down to one or less chunks we can't shorten it any further.
_, [], [_], _ | _, [], [], _ ->
case shortened {
True -> Ok(#(left, right))
False -> Error(Nil)
}
// We always want to keep the rightmost chunk. So we make sure to
// never drop it if it's the last one remaining on the right.
_, left, [_] as right, Right ->
do_shorten(left, right, shortened, Left, current_length, max_length)
// Otherwise we remove a piece from the end specified by `end` until we
// reach the desired size.
_, [dropped, ..left], right, Left -> {
let new_length = current_length - string.length(dropped)
do_shorten(left, right, True, Right, new_length, max_length)
}
_, left, [dropped, ..right], Right -> {
let new_length = current_length - string.length(dropped)
do_shorten(left, right, True, Left, new_length, max_length)
}
// If we can't remove from the desired side we try shrinking further from
// the other end.
_, [], right, Left ->
do_shorten([], right, shortened, Right, current_length, max_length)
_, left, [], Right ->
do_shorten(left, [], shortened, Left, current_length, max_length)
}
}