Lustre's CLI and development tooling: zero-config dev server, bundling, and scaffolding.
at main 3.9 kB view raw
1import gleam/list 2import gleam/result 3import gleam/string 4import term_size 5 6pub fn term_width() -> Int { 7 term_size.columns() 8 |> result.unwrap(80) 9} 10 11pub fn shorten_url(url: String, to max_length: Int) -> String { 12 let chunks = case url { 13 "https://" <> rest -> ["https:/", ..string.split(rest, on: "/")] 14 _ -> string.split(url, on: "/") 15 } 16 17 // We want to reduce the max length further to offset the `/` we'll 18 // be adding back to join the remaining pieces. 19 let max_length = max_length - list.length(chunks) 20 case shorten(chunks, to: max_length) { 21 Error(_) -> url 22 Ok(#(left, right)) -> 23 string.join(left, "/") <> "/.../" <> string.join(right, "/") 24 } 25} 26 27/// Shortens a list of strings by removing pieces from the middle until it gets 28/// down to the desired length, returning its two remaining halves. 29/// Returns an error if it couldn't shorten the list. 30/// 31fn shorten( 32 strings: List(String), 33 to max_length: Int, 34) -> Result(#(List(String), List(String)), Nil) { 35 let initial_length = 36 list.fold(over: strings, from: 0, with: fn(acc, string) { 37 acc + string.length(string) 38 }) 39 40 // We want to divide the strings in two halves and remove items starting from 41 // the middle going in both directions until we get to the desired length or 42 // we can shorten it any further. 43 let middle = list.length(strings) / 2 44 let #(left, right) = list.split(strings, middle) 45 // It's important we reverse the left part because we want to remove pieces 46 // from its end, that is the part nearer to the middle of `strings`. 47 let left = list.reverse(left) 48 case do_shorten(left, right, False, Right, initial_length, max_length) { 49 // Remember that the left part was reversed so that `do_shorten` could 50 // remove items from its start! We have to reverse it back to normal. 51 Ok(#(new_left, new_right)) -> Ok(#(list.reverse(new_left), new_right)) 52 Error(Nil) -> Error(Nil) 53 } 54} 55 56type End { 57 Left 58 Right 59} 60 61/// Drops strings from the start of each list in turns until it shrinks it down 62/// to the desired size. 63/// If it could acutally shorten the lists it returns `Ok` wrapping them, 64/// otherwise it returns `Error(Nil)`. 65/// 66fn do_shorten( 67 left: List(String), 68 right: List(String), 69 shortened: Bool, 70 from: End, 71 current_length: Int, 72 max_length: Int, 73) -> Result(#(List(String), List(String)), Nil) { 74 case current_length <= max_length, left, right, from { 75 // If we're already shorter than the maximum allowed length we don't shrink 76 // the lists any further. 77 True, _, _, _ -> 78 case shortened { 79 True -> Ok(#(left, right)) 80 False -> Error(Nil) 81 } 82 83 // If we're down to one or less chunks we can't shorten it any further. 84 _, [], [_], _ | _, [], [], _ -> 85 case shortened { 86 True -> Ok(#(left, right)) 87 False -> Error(Nil) 88 } 89 90 // We always want to keep the rightmost chunk. So we make sure to 91 // never drop it if it's the last one remaining on the right. 92 _, left, [_] as right, Right -> 93 do_shorten(left, right, shortened, Left, current_length, max_length) 94 95 // Otherwise we remove a piece from the end specified by `end` until we 96 // reach the desired size. 97 _, [dropped, ..left], right, Left -> { 98 let new_length = current_length - string.length(dropped) 99 do_shorten(left, right, True, Right, new_length, max_length) 100 } 101 _, left, [dropped, ..right], Right -> { 102 let new_length = current_length - string.length(dropped) 103 do_shorten(left, right, True, Left, new_length, max_length) 104 } 105 106 // If we can't remove from the desired side we try shrinking further from 107 // the other end. 108 _, [], right, Left -> 109 do_shorten([], right, shortened, Right, current_length, max_length) 110 _, left, [], Right -> 111 do_shorten(left, [], shortened, Left, current_length, max_length) 112 } 113}