+
    Ki9  c                   s   R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RI	H	t
 ^ RIHtHtHtHt ^ RIHt ^ RIHt ]
! R4      tR R	 ltR
 R ltR R ltR.R R lltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R lt ] ! R  R!4      4       t!R" R# lt"R/R$ R% llt#R& R' lt$R( R) lt%R* R+ lt&R, R- lt'R# )0zString utilitiesN)	dataclass)gettext)ListOptionalTupleUnion)GLib)loggerzNever playedc                0    V ^8  d   QhR\         R\         /#    valuereturnstrformat   "8/usr/lib/python3.14/site-packages/lutris/util/strings.py__annotate__r      s     ; ; ; ;    c                 sp    \        \        P                  ! \        P                  \        V 4      4      4      # N)r   uuidZuuid5ZNAMESPACE_URL)r      &r   get_uuid_from_stringr      s"    tzz$,,c%j9::r   c                r   r	   r   r   r   r   r   r      s      3 3 r   c                s\   \        V 4      p\        P                  ! RV4      P                  RR4      pVP	                  R4      p\        \
        P                  ! RRV4      4      P                  4       P                  4       p\
        P                  ! RRV4      pV'       g   \        V 4      pV# )	zRemove special characters from a string and slugify it.

Normalizes string, converts to lowercase, removes non-alpha characters,
and converts spaces to hyphens.
ZNFDZasciiZignorezutf-8z[^\w\s-] z[-\s]+Z-)
r   unicodedata	normalizeZencodeZdecodereZsubstripZlowerr   )r   Z_valueZslug   &  r   slugifyr      s     ZF ""5&188(KF]]7#FR01779??AF66)S&)D $E*Kr   c                r   r	   r   r   r   r   r   r   /   s        r   c                sL   V '       d   \         P                  ! RV 4      pRpRp\        VP                  4       4       FW  p\         P                  ! V4      pV'       d   K#  VP                  4       pV'       d   V'       g
   VR,          pMW$,          pTpKY  	  VRRR1,          # V # )a  Returns a string that is 'value', but with combining characters removed.

This normalizes the text to form KD, which also removes any compatibility characters,
so things like ellipsis are expanded to '...'.

This also strips leading a trailing whitespace, and normalizes all remaining whitespace
to single spaces.

This does allow non-ASCII characters (like Greek or Cyrillic), and does not interfere with
casing.

We use this for a more forgiving search.
ZNFKDr   F Ni)r   r   Zreversedr   	combiningisspace)r   Z
decomposedZresultZprev_whitespaceZchr!   Z
whitespaces   &      r   strip_accentsr#   /   s      **659
:++-.B#--b1I9ZZ\
*#LF", / dd|Lr   c                s<    V ^8  d   QhR\         R\        R\         /# )r
   r   number_widthr   r   intr   r   r   r   r   O   s!       3  r   c                s   a R V3R llp\         P                  ! RV 4       Uu. uF
  q2! V4      NK  	  ppRP                  V4      # u upi )zaReturns a string with the numerical parts (runs of digits)
0-padded out to 'number_width' digits.c                r   r
   textr   r   r   r   r   r   Z*get_natural_sort_key.<locals>.__annotate__S   s     O O# O# Or   c                 sr   < V P                  4       '       d   V P                  S4      # V P                  4       # r   )isdigitZzfillcasefold)r(   r$   s   &r   pad_numbersZ)get_natural_sort_key.<locals>.pad_numbersS   s'    +/<<>>tzz,'Nt}}Nr   z([0-9]+)r   )r   splitjoin)r   r$   r+   ZcZrunss   &f   r   get_natural_sort_keyr.   O   sJ    O O %'HHZ$?@$?qKN$?D@774= As   Ac                sR    V ^8  d   QhR\         R\         R\        \         ,          /# )r
   stringr(   r   r   r   r   r   r   r   r   Z   s%     < <3 <c <d3i <r   c                sb    VP                  R4      pV Uu. uF  q0V9   g   K  VNK  	  up# u upi )zFReturn each full line where a string was found in the multi-line text.Z
r,   )r/   r(   Zinput_linesZlines   &&  r   lookup_strings_in_textr2   Z   s.    **T"K(;[TdNDD[;;;s   ,,c                sr    V ^8  d   QhR\         R\        \        \        ,          \         \         3,          /# )r
   versionr   )r   r   r   r&   r   r   r   r   r   `   s.     G G3 G5cC)<#= Gr   c                s@   \         P                  ! RV 4      pV'       g   . RR3# VP                  4       ^ ,          pV ^ VP                  4       ^ ,           pWP                  4       ^,          R pVP	                  R4       Uu. uF  p\        V4      NK  	  upWC3# u upi )aX  Parse a version string

Return a 3 element tuple containing:
 - The version number as a list of integers
 - The prefix (whatever characters before the version number)
 - The suffix (whatever comes after)

 Example::
    >>> parse_version("3.6-staging")
    ([3, 6], '', '-staging')

Returns:
    tuple: (version number as list, prefix, suffix)
z(\d[\d\.]+\d)r   NZ.)r   ZsearchZgroupsZspanr,   r&   )r3   Zversion_matchZversion_numberZprefixZsuffixp   &     r   parse_versionr6   `   s     II.8M2rz"))+A.NQ++-a01F'')!,./F*00565qCF56FF6s   Bc          	      s    V ^8  d   QhR\         R\        \        \         \        \         R3,          3,          ,          /# )r
   r/   r   .)r   r   r   r   r   r   r   r   r   x   s5     S S SU3c3h3G-H(I Sr   c                s    R R lpV '       g   . # V P                  R4       Uu. uF
  q!! V4      NK  	  up Uu. uF  q"'       g   K  VNK  	  up# u upi u upi )aG  Parse a string to allow for complex dependencies
Works in a similar fashion as Debian dependencies, separate dependencies
are comma separated and multiple choices for satisfying a dependency are
separated by pipes.

Example: quake-steam | quake-gog, some-quake-mod returns:
    [('quake-steam', 'quake-gog'), 'some-quake-mod']
c                sl    V ^8  d   QhR\         R\        \         \        \         R3,          3,          /# )r
   depr   .)r   r   r   r   r   r   r   Z)unpack_dependencies.<locals>.__annotate__   s,       sE#s(O';!< r   c                 s    R V 9   dK   \         ;QJ d#    . R V P                  R 4       4       F  NK  	  5# ! R V P                  R 4       4       4      # V P                  4       # )Z|c              3   sp   "   T F,  qP                  4       '       g   K  VP                  4       x  K.  	  R # 5ir   )r   )Z.0Zoption   & r   Z	<genexpr>Z;unpack_dependencies.<locals>._expand_dep.<locals>.<genexpr>   s      WnFns   66)Ztupler,   r   )r7   r   r   _expand_depZ(unpack_dependencies.<locals>._expand_dep   sG    #:5WciinW5W5WciinWWWyy{r   Z,r1   )r/   r9   r7   r   r   unpack_dependenciesr:   x   sQ    
 	8>S8IJ8IK,8IJRJCcCCJRRJRs   A	AAc                r   r'   r   r   r   r   r   r      s     . .3 .3 .r   c                sT    V '       g   R# \         P                  ! \        V 4      4      # )z_Return a string ready to used in Gtk widgets, with anything that could
be Pango markup escaped.r   )r   Zmarkup_escape_textr   )r(   r   r   gtk_safer;      s     ""3t9--r   c                r   r'   r   r   r   r   r   r      s        r   c                s    V '       g   R# \         P                  ! RV 4      p\        V4       F=  w  r#\        V4      ^ 8  g   K  \	        V4      pV^,          ^ 8w  d
   RV RV R2pW1V&   K?  	  RP                  V4      # )z]Escapes the text as with gtk_safe, but detects URLs and converts them to
anchor tags as well.r   zP(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)z	<a href="z">z</a>)r   r,   Z	enumeratelenr;   r-   )r(   partsZindexZpart   &   r   gtk_safe_urlsr?      sw     HHlnrsE 't9q=D>DqyA~"4&4&5%L ( 775>r   c                0    V ^8  d   QhR\         R\        /# r'   )r   boolr   r   r   r   r      s        r   c                 s$   R  R lp\        V 4      ^ 8X  d   R#  \        P                  ! 4       p^ p\        P                  P	                  W#RVR7      pRV  R2pVP                  V\        V4      4       R#   \        P                   d     R# i ; i)c                s     V ^8  d   QhRRRR/# )r
   
_user_dataNr    r   r   r   r   Z+is_valid_pango_markup.<locals>.__annotate__   s       $ r   c                 s    R # r   rC   )rB   r   r   destroy_funcZ+is_valid_pango_markup.<locals>.destroy_func   s    r   TN)parserZflagsZ	user_dataZuser_data_dnotifyz<markup>z	</markup>F)r<   r   ZMarkupParserZMarkupParseContextZnewZparseZGError)r(   rD   rE   Zparser_flagsZcontextZmarkupr5   r   is_valid_pango_markuprF      s     4yA~""$ /0))--Q] . 
 D6+fc&k*;; s   AA6 6BBc                r@   )r
   playtimer   floatr   r   r   r   r   r      s      U s r   c                s   V '       g   \         #  \        V 4      p \
        P                  ! T 4      p\        \        Y,
          ^<,          ^ 4      4      pT^<8  d   T^<,          pT^,          pT^8X  d   \        R4      M
\        R4      pT^ 8  d   T RT 2MRpT^8X  d   \        R4      M
\        R4      pT^ 8  d   T RT 2MRpRP                  YF3 Uu. uF  qw'       g   K  TNK  	  up4      pT'       d   T# T '       d   \        R4      # \         #   \         d!    \        P                  ! RT 4       \         u # i ; iu upi )	z.Return a human-readable value of the play timezInvalid playtime value '%s'hourhoursr    r   minuteminutesLess than a minute)NO_PLAYTIMErI   
ValueErrorr   warningmathZfloorr&   round_r-   )	rG   rK   rM   Z
hours_unitZ
hours_textZminutes_unitZminutes_textr(   Zformatted_times	   &        r   get_formatted_playtimerU      s&   ?
 JJx E%)R/34G "}2
#qj6ajJ,1AIE7!J<(2J")Q,1X;AiLL29A+gYa~.2LXX0JS0Jdtt0JSTN%&&/  4h?$ Ts   D 	E$E(D?>D?c                r@   r'   )r   rI   r   r   r   r   r      s     & & & &r   c                s8    \        V 4      pVP                  4       # )z$Parses a textual playtime into hours)parse_playtime_partsget_total_hours)r(   rG   r8   r   parse_playtimerX      s    #D)H##%%r   c                   s   a  ] tR t^t o RtRtRtRtRtRt	V 3R lR lt
V 3R lR ltV 3R lR ltV 3R lR	 ltV 3R
 ltRtV tR# )PlaytimeParts        c                    < V ^8  d   QhRS[ /# r
   r   )rA   r   __classdict__   "r   r   PlaytimeParts.__annotate__   s     h h$ hr   c                s    V P                   ;'       g]    V P                  ;'       gI    V P                  ;'       g5    V P                  ;'       g!    V P                  ;'       g    V P
                  '       * # r   )yearsmonthsweeksdaysrK   rM   selfr   r   is_emptyZPlaytimeParts.is_empty   sN    JJff$++fffftyyffDJJffZ^ZfZfggr   c                r[   r\   rI   r]   r_   r   r   r`      s     
 
 
r   c                s4   V P                   V P                  ^<,          ,           V P                  ^,          ,           V P                  ^,          ^,          ,           V P                  ^,          ^,          ,           V P
                  ^,          R,          ,           # )i<   im  )rK   rM   rd   rc   rb   ra   re   r   r   rW   ZPlaytimeParts.get_total_hours   su    JJllR ii"n jj2o!" kkB#	$
 jj2o#$	
r   c                s&   < V ^8  d   QhRS[ RS[/# )r
   rK   r   )rI   rA   r]   r_   r   r   r`      s     D DU Dt Dr   c                sd   V P                  4       pV P                  R 8w  d0   \        P                  ! V^<,          \	        V^<,          ^ 4      4      # V P
                  R 8w  d"   \        P                  ! V\	        V^ 4      4      # V P                  R 8w  d0   \        P                  ! V^,          \	        V^,          ^ 4      4      # V P                  R 8w  d0   ^p\        P                  ! W#,          \	        W,          ^ 4      4      # V P                  R 8w  d0   Rp\        P                  ! W$,          \	        W,          ^ 4      4      # V P                  R 8w  d0   Rp\        P                  ! W%,          \	        W,          ^ 4      4      # \        P                  ! V^<,          \	        V^<,          ^ 4      4      # )rZ   i  i8"  )
rW   rM   rR   ZiscloserS   rK   rd   rc   rb   ra   )rf   rK   Ztotal_hoursZhours_per_weekZhours_per_monthZhours_per_years   &&    r   matchesZPlaytimeParts.matches   s@   **,<<3<<b 0%
A2FGG::<<U5!_==99<<b 0%
A2FGG::#N<< <eEDZ\]>^__;;#%O<< =uUE\^_?`aa::%N<< <eEDZ\]>^__ ||K",eEBJ.BCCr   c                s,   < V ^8  d   QhRS[ RS[RS[/# )r
   numunitr   )rI   r   rA   r]   r_   r   r   r`     s"      E   r   c                s   R RRR\        R4      \        R4      .pRRRR\        R4      \        R4      .pR\        R	4      \        R
4      .pR\        R4      \        R4      .pR\        R4      \        R4      .pR\        R4      \        R4      .pW#9   d   V ;P                  V,          un        R# W$9   d   V ;P                  V,          un        R# W%9   d   V ;P                  V,          un        R# W&9   d   V ;P                  V,          un        R# W'9   d   V ;P
                  V,          un        R# W(9   d   V ;P                  V,          un        R# R# )ZhZhrrK   rJ   ZmZminrL   rM   ZdZdayrd   ZwkZweekrc   ZmoZmonthrb   ZyrZyearra   FT)rT   rK   rM   rd   rc   rb   ra   )	rf   rj   rk   Z
hour_unitsZminute_unitsZ	day_unitsZ
week_unitsZmonth_unitsZ
year_unitss	   &&&      r   add_partZPlaytimeParts.add_part  s-    4&!F)QwZH
UHi8a	lS!E(AfI.	AfIqz2
QwZ85AfIqz2
JJ#J  !LLCL  III  JJ#J   KK3K  JJ#J  r   c                sb   < V ^8  d   Qh/ S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   # )r
   ra   rb   rc   rd   rK   rM   rh   r]   r_   r   r   r`      sQ         	 
      r   rC   N)Z__name__Z
__module__Z__qualname__Z__firstlineno__ra   rb   rc   rd   rK   rM   rg   rW   ri   rl   Z__annotate_func__Z__static_attributes__Z__classdictcell__)r^   s   @r   rY   rY      sW     EFEDEGh h
 
D D* W  r   rY   c                r@   r'   )r   rY   r   r   r   r   r   +  s     9 9s 9} 9r   c                 s   V P                  4       P                  4       p \        4       p\        R 4      P                  4       V 8X  d   V# \        P                  4       V 8X  d   V# \        R4      V ,          p \        V 4      Vn        V#   \         d     Mi ; iT P                  R4      p\        T4      ^8X  d   T^ ,          P                  4       P                  4       '       d|   T^,          P                  4       P                  4       '       dQ    \        T^ ,          4      Tn        \        T^,          4      Tn        T#   \         d   p\        T4      ThRp?ii ; i\        P                  ! RT 4       Uu. uF4  qU'       g   K  TP                  4       '       d   K$  TP                  4       NK6  	  Mu upi pp\        T4      p  \!        T4      p \        T4      pM  \         d   p\        T4      ThRp?ii ; i \!        T4      p	M<  \"         d/   pTP%                  4       '       g   Rp	 Rp?M\        T4      ThRp?ii ; iTP'                  Y4      '       d   K  \        T4      h  \"         d     T# i ; i)rN   z'%s' is not a valid playtime.Z:Nz
([0-9.,]+)rM   )r   r*   rY   rT   rO   rI   rK   rP   r,   r<   r)   r&   rM   r   r"   ZiterZnextZStopIterationrg   rl   )
r(   rG   Zerror_messager=   exr4   Z
parts_iterZnum_textrj   rk   
   &         r   rV   rV   +  s   ::<  "DH	'')T1%56=Mt  JJsOE
5zQ58>>+3355%(..:J:R:R:T:T	4 q]HN"58}HO 	4]+3	4 !#t <V <1Y!))+YQWWY <VEVeJJ'H8Ho 8 /R78<J'  <((**$D$]3;	< $$S// // Os   7B	 	BB/E EEE:	F5F5 F5I- G!  I- !G=,G88G==I- H I- II0I- 5III- "I- -I<;I<c          	      s    V ^8  d   QhR\         R\         R\        \        \         ,          ,          R\        \         ,          /# )r
   argsclosing_quot
quotationsr   )r   r   r   r   r   r   r   r   g  s9     
 
3 
c 
HTRUYDW 
cghkcl 
r   c                 s.   Vf   RR.p \         P                  ! W,           4      #   \         dd   pTP                  ^ ,          pTR8X  d(   T'       d    \	        Y^ ,          TR,          4      u R p?# \
        P                  ! T4       . u R p?# R p?ii ; i)NZ'Z"zNo closing quotation:i   NN)shlexr,   rP   ro   _split_argumentsr   Zerror)ro   rp   rq   rm   Zmessages   &&&  r   rs   rs   g  sv    3Z
{{4.// ''!*,,#DQ-BHHW	s,   & B!BB,B2B	BBc                sF    V ^8  d   QhR\         R\        \         ,          /# )r
   ro   r   r0   r   r   r   r   r   t  s     " "# "$s) "r   c                s,    V '       g   . # \        V 4      # )z:Wrapper around shlex.split that is more tolerant of errors)rs   )ro   r   r   split_argumentsrt   t  s    	D!!r   c                r@   r
   sizer   rH   r   r   r   r   r   |  s     2 2U 2s 2r   c                sb    Rp^ pV R8  d   V R,          p V^,          pK  RWV,          3,          # )z,Shows a size in bytes in a more readable way   z%0.1f %s)	bytesZkBZMBZGBZTBZPBznuh uhzno wayZBSrC   )rv   units
unit_indexr   r   
human_sizer{   |  s8    MEJ
+d{a
Z0111r   c                r@   ru   r%   r   r   r   r   r     s        r   c                sr   Rp^ pV P                  4       p V P                  4       p V FB  pV P                  V4      '       d    V R\        V4      )  P                  4       p  MV^,          pKD  	   \	        \        V 4      RV,          ,          4      #   \         d    \        P                  ! RT 4        ^ # i ; i)z'Converts a human readable size to bytesNrw   zInvalid size value '%s')rx   ZkbZmbZgbZtbZpb)	r   r*   Zendswithr<   r&   rI   rP   r   rQ   )rv   ry   rz   rk   r>   r   computer_sizer|     s    3EJ::<D==?D==3t9*%++-Da
	 
5;$
"2344 0$7s   .!B "B65B6c                r@   )r
   	timestampr   rH   r   r   r   r   r     s     $ $ $# $r   c                 s   \         P                   ! 4       V ,
          pTpV^ 8  d   \        R4      # V^8  d   \        R4      # . pRpRp^ p^ pV^V,          8  dB   \        W,          4      pWV,          ,
          pVP                  \        R4      V,          4       V^V,          8  dB   \        W,          4      pWV,          ,
          pVP                  \        R4      V,          4       V'       gs   V^8  dl   V^<8  de   \        V^<,          4      pW^<,          ,
          pV^8w  d#   VP                  \        R4      V,          4       MVP                  \        R4      4       V^Z8  dO   \        V4      p	V	^8w  d#   VP                  \        R4      V	,          4       MVP                  \        R	4      4       VP                  \        R
4      4       RP	                  V4      # )i    zin the futurezjust nowi  z%d daysz%d hoursz
%d minutesz1 minutez
%d secondsz1 secondZagor    iQ )timerT   r&   Zappendr-   )
r}   Z
time_deltaZoriginal_time_deltar=   Zday_in_secondsZhour_in_secondsrd   rK   rM   Zsecondsrn   r   time_agor     s}   y(J$A~!!A~}ENODEQ'':/0"77
Qy\D()A''J12/"99
Qz]U*+EAI*r/jB&'B,.
a<LL<723LL:'Rj/a<LL<723LL:'	LL588E?r   )i   )r   N)(Z__doc__rR   r   rr   r~   r   r   Zdataclassesr    r   rT   Ztypingr   r   r   r   Zgi.repositoryr   Zlutris.util.logr   rO   r   r   r#   r.   r2   r6   r:   r;   r?   rF   rU   rX   rY   rV   rs   rt   r{   r|   r   rC   r   r   <module>r      s      	     !   / /  ";.@<G0S(.$.B& B B BJ9x
"2$$r   