
    i                        d Z ddlmZ ddlZddlmZ ddlmZmZ ddl	m	Z	 ddl
mZmZ ddlZddlmZmZmZ dd	lmZmZ d
dlmZmZ d
dlmZ d
dlmZmZmZ erddlmZ e G d de                      ZdS )zBMain Searcher class for icalendar component filtering and sorting.    )annotationsN)Iterable)	dataclassfield)datetime)TYPE_CHECKINGAny)Calendar	ComponentTimezone)DATE_MAX_DTDATE_MIN_DT   )	Collationget_sort_key_function)FilterMixin)_iterable_or_false_normalize_dttypes_factory)CalendarObjectResourcec                  :   e Zd ZU dZdZded<   dZded<   dZded<   dZded<   dZ	ded	<   dZ
ded
<   dZded<   dZded<   dZded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<    ee          Zded<   	 	 	 	 dGdHd)Z	 	 	 	 dIdJd+Z	 	 dKdLd1Z	 dMdNd5ZdOd8ZdPd:ZdOd;Z dQd>Z!dRd@Z"dSdBZ#dTdFZ$dS )USearchera  This class will:

    * Allow to build up a calendar search query.
    * Help filterering or sorting a list of calendar components

    Primarily VJOURNAL, VTODO and VEVENT Calendar components are
    intended to be supported, but we should consider if there is any
    point supporting FREEBUSY as well.

    This class is a bit stubbed as of 2025-11, many things not working
    yet.  This class was split out from the CalDAV library and is
    intended for generic search logic not related to the CalDAV
    protocol.  As of 2025-11, the first priority is to make the bare
    minimum of support needed for supporting refactorings of the
    CalDAV library.

    Long-term plan is to also allow for adding subqueries that can be
    bound together with logical AND or OR.  (Will AND every be needed?
    After all, add different criterias to one Searcher object, and all
    of them has to match)

    Properties (like SUMMARY, CATEGORIES, etc) are not meant to be
    sent through the constructor, use the :func:`icalendar_searcher.Searcher.add_property_filter`
    method.  Same goes with sort keys, they can be added through the
    `func:icalendar_searcher.Searcher.add_sort_key` method.

    The ``todo``, ``event`` and ``journal`` parameters are booleans
    for filtering the component type.  If i.e. both todo and
    journal is set to True, everything but events should be returned.
    If none is given (the default), all objects should be returned.

    If ``todo`` is given ``include_completed`` defaults to False,
    which means completed tasks will be tfiltered out.

    ``start`` and ``end`` is giving a time range.  RFC4791, section
    9.9 gives a very clear and sane definitions of what should be
    returned when searching a CalDAV calendar for contents over a time
    span.  While this package is not related to CalDAV pper se, the
    logic seems sane, so we will stick to that one.  Timestamps should
    ideally be with a time zone, if not given the local time zone will
    be assumed.  All-day events may be tricky to get correct when
    timestamps are given and calendar data covers multiple time zones.

    ``alarm_start`` and ``alarm_end`` is similar for alarm searching

    If ``expand`` is set to True, recurring objects will be expanded
    into reccurence objects for the time period specified by ``start``
    and ``end``.  Generators are used, so ``expand`` may even work
    without ``start`` and ``end`` set if care is taken
    (i.e. converting the generator to a list may cause problems)

    Unless you now what you are doing, ``expand`` should probably be
    set to true, and start and end should be given and shouldn't be
    too far away from each other.  Currently the sorting algorithm
    will blow up if the expand yields infinite number of events.  It
    may probably blow up even if expand yields millions of events.  Be
    careful.

    For filtering an icalendar instance ``mycal`` containing a
    VCALENDAR with multiple independent events, one may do
    ``searcher.filter([mycal])``.  The CalDAV library contains a
    CalDAVSearcher class inheritating this class and including a
    ``.search(caldav_calendar)`` method.  The idea is that it may be
    done in the same way also for searching other calendars or
    calendar-like systems using other protocols.

    The filtering and sorting methods should accept both wrapper
    objects (for the CalDAV library, those are called
    CalendarObjectResource and contains extra information like the URL
    and the client object) and ``icalendar.Calendar`` objects from the
    icalendar library.  Wrapper objects should have the
    ``icalendar.Calendar`` object available through a property
    ``icalendar_instance``.  Even for methods expecting a simple
    component, a ``Calendar`` should be given.  This is of
    imporantance for recurrences with exceptions (like, "daily meeting
    is held at 10:00, except today the meeting is postponed until
    12:00" may be represented through a calendar with two
    subcomponents)

    Methods expecting multiple components will always expect a list of
    calendars (CalDAV-style) - but the algorithms should also be
    robust enough to handle multiple independent components embedded
    in a single Calendar.

    Other ideas that one may consider implementing:
    * limit, offset.
    * fuzzy matching

    Nbooltodoeventjournalr   startendalarm_start	alarm_endinclude_completedFexpand)default_factorylist
_sort_keysdict_sort_collation_sort_locale_sort_case_sensitive_property_filters_property_operator_property_collation_property_locale_property_case_sensitivecontainsTkeystrvaluer	   operatorcase_sensitive	collationCollation | Nonelocale
str | NonereturnNonec                <   |                                 }|dvrt          d| d          |dk    r|dk    rdn|}|dk    rPt          |t                    r;t	          j        |          } ||                    |                    | j        |<   n6|dk    r|| j        |<   n% t	          j        |          |          | j        |<   || j        |<   | || j	        |<   || j
        |<   || j        |<   dS t          j        | j	        |<   d| j
        |<   || j        |<   dS )u  Adds a filter for some specific iCalendar property.

        Examples of valid iCalendar properties: SUMMARY,
        LOCATION, DESCRIPTION, DTSTART, STATUS, CLASS, etc

        :param key: must be an icalendar property, i.e. SUMMARY
                   Special virtual property "category" (singular) is also supported
                   for substring matching within category names
        :param value: should adhere to the type defined in the RFC
        :param operator: Comparision operator ("contains", "==", etc)
        :param case_sensitive: If False, text comparisons are case-insensitive.
        :param collation: Advanced collation strategy for text comparison.
                         If specified, overrides case_sensitive parameter.
                         Only needed by power users for locale-aware collation.
        :param locale: Locale string (e.g., "de_DE") for locale-aware collation.
                      Only used with collation=Collation.LOCALE.

        The case_sensitive parameter only applies to text properties.
        The default has been made for case sensitive searches.  This
        is contrary to the CalDAV standard, where case insensitive
        searches are considered the default, but it's in accordance
        with the CalDAV library, where case sensitive searches has
        been the default.

        **Special handling for categories:**

        - **"categories"** (plural): Exact category name matching
          - "contains": subset check (all filter categories must be in component)
          - "==": exact set equality (same categories, order doesn't matter)
          - Commas in filter values split into multiple categories

        - **"category"** (singular): Substring matching within category names
          - "contains": substring match (e.g., "out" matches "outdoor")
          - "==": exact match to at least one category name
          - Commas in filter values treated as literal characters

        For the operator, the following is (planned to be) supported:

        * contains - will do a substring match (A search for "summary"
          "contains" "rain" will return both events with summary
          "Training session" and "Singing in the rain")

        * == - exact match is required

        * ~ - regexp match

        * <, >, <=, >= - comparision

        * <> or != - inqueality, both supported

        * def, undef - will match if the property is (not) defined.  value can be set to None, the value will be ignored.

        Examples:
            # Case-insensitive search (simple API)
            searcher.add_property_filter("SUMMARY", "meeting", case_sensitive=False)

            # Case-sensitive search (default)
            searcher.add_property_filter("SUMMARY", "Meeting")

            # Advanced: locale-aware collation (requires PyICU)
            searcher.add_property_filter("SUMMARY", "Müller",
                                        collation=Collation.LOCALE,
                                        locale="de_DE")

        )r/   undefz==zThe operator z is not supported yet.r<   category
categoriesN)lowerNotImplementedError
isinstancer1   r   for_property	from_icalr*   r+   r,   r-   r.   r   SIMPLE)	selfr0   r2   r3   r4   r5   r7   property_keyfacts	            V/root/projects/butler/venv/lib/python3.11/site-packages/icalendar_searcher/searcher.pyadd_property_filterzSearcher.add_property_filter   s^   f iikk666%&Vh&V&V&VWWWw+.*+<+<<<#L l""z%'='=" %1,??.2d4>>%3H3H.I.I&s++
"" /4&s++.Vm.H.V.VW\.].]&s+'/$  ,5D$S))/D!#&1?D)#... -6,<D$S))-D!#&1?D)#...    reversedc                "   |                                 }|t          j        v s|dv sJ | j                            ||f           | || j        |<   || j        |<   || j        |<   dS t          j	        | j        |<   d| j        |<   || j        |<   dS )a  Add a sort key for sorting components.

        Special keys "isnt_overdue" and "hasnt_started" is
        supported, those will compare the DUE (for a task) or the
        DTSTART with the current wall clock and return a bool.

        Except for that, the sort key should be an icalendar property.

        :param key: The property name to sort by
        :param reversed: If True, sort in reverse order
        :param case_sensitive: If False, text sorting is case-insensitive.
                              Only applies to text properties. Default is True.
        :param collation: Advanced collation strategy for text sorting.
                         If specified, overrides case_sensitive parameter.
        :param locale: Locale string (e.g., "de_DE") for locale-aware sorting.
                      Only used with collation=Collation.LOCALE.

        Examples:
            # Case-insensitive sorting (simple API)
            searcher.add_sort_key("SUMMARY", case_sensitive=False)

            # Case-sensitive sorting (default)
            searcher.add_sort_key("SUMMARY")

            # Advanced: locale-aware sorting (requires PyICU)
            searcher.add_sort_key("SUMMARY", collation=Collation.LOCALE, locale="de_DE")
        )isnt_overduehasnt_startedN)
r?   r   	types_mapr%   appendr'   r(   r)   r   rD   )rE   r0   rK   r4   r5   r7   s         rH   add_sort_keyzSearcher.add_sort_key   s    F iikkm--- 9
 2
 2
 2
 2
 	X///  (1D %%+Dc"-;D%c*** )2(8D %%)Dc"-;D%c***rJ   	component-Calendar | Component | CalendarObjectResourceexpand_only_ignore_rrule_and_timeIterable[Component]c                j                          |          }|r	 j        s|S dD ][}t           |          }|rGt          |t                    st          j        d           t           |t          |                     \|}d|d         }|s,d|v r(|s& 	                    |d          }	|	s|dd	         }nd j
         j          _
        d
}
t           fd|
D                       r'|
D ]#}t           |          t           |d           $n&|
D ]#}t           |          t           |d           $t           fd|
D                       |rg dn}|sd|v r                     ||          }|s|s j        s j        r fd|D             }t#           fd|
D                       sfd|D             } fd|D             } j        s j        r fd|D             }|s j        s j        r fd|D             } j        rt-          |          S t/          |d	          r|S d	S )a  Checks if one component (or recurrence set) matches the
        filters.  If the component parameter is a calendar containing
        several independent components, an Exception may be raised,
        though recurrence sets should be suppored.

        * If there is no match, ``None`` or ``False`` will be returned
          (the exact return value is currently not clearly defined,
          but ``bool(return_value)`` should yield False).

        * If a time specification is given and the component given is
          a recurring component, it will be expanded internally to
          check if it matches the given time specification.

        * If there is a match, the component should be returned
          (wrapped in a tuple) - unless ``expand`` is set, in which
          case all matching recurrences will be returned (as a
          generator object).

        :param component: Todo, Event, Calendar or such
        :param expand_only: Don't do any filtering, just expand

        )r   r   r   r    zJDate-range searches not well supported yet; use datetime rather than datesFr   RRULET)rU   r   N)r   r   r   c              3  8   K   | ]}t          |          V  d S Ngetattr.0xrE   s     rH   	<genexpr>z+Searcher.check_component.<locals>.<genexpr>  s-      44AwtQ444444rJ   c                ^    g | ])}t          |          d |                                 *S )V)r\   upperr]   s     rH   
<listcomp>z,Searcher.check_component.<locals>.<listcomp>  s6    RRRaqAQAQR/aggii//RRRrJ   )VTODOVEVENTVJOURNALc              3  F   K   | ]}                     |          |V  d S rZ   )_check_ranger]   s     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  s7      !T!Tt?P?PQR?S?S!T!!T!T!T!T!T!TrJ   c              3  8   K   | ]}t          |          V  d S rZ   r[   r]   s     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  s-      <<AwtQ''<<<<<<rJ   c              3  .   K   | ]}|j         v |V  d S rZ   )name)r^   r_   
comptypesus     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  s/      !T!Tqv?S?S!?S?S?S?S!T!TrJ   c              3  F   K   | ]}                     |          |V  d S rZ   )_check_completed_filterr]   s     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  s7      [[A4;W;WXY;Z;Z[a[[[[[[rJ   c              3  J   K   | ]}                     |           |V  dS ))
skip_undefN)_check_property_filters)r^   r_   rE   skip_undef_for_expandeds     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  sQ       " "33ABY3ZZ"" " " " " "rJ   c              3  F   K   | ]}                     |          |V  d S rZ   )_check_alarm_ranger]   s     rH   r`   z+Searcher.check_component.<locals>.<genexpr>  s7      !Z!Zt?V?VWX?Y?Y!Z!!Z!Z!Z!Z!Z!ZrJ   )!_validate_and_normalize_componentr"   r\   rA   r   loggingwarningsetattrr   check_componentr!   r   anyset_expand_recurrencesr   r   allr*   r+   r   r    r   next)rE   rR   rT   rU   orig_recurrence_setattrr2   recurrence_setfirstbase_element_match
comptypeslr_   comptypes_for_expansionrm   rs   s   `            @@rH   rz   zSearcher.check_component2  st   L #DDYOO  	't{ 	'&& A 	: 	:DD$''E :!%22 Od   dM%$8$8999 -: #(#A& 
	/w%//8N/!%!5!5eTX!5!Y!Y% /!/!3
 +/' !))-]D" 2
444444444 	+ , ,4##+D!U+++,   + +4##+D!T***RRRR:RRRSS
 FQ"`"A"A"A"AV`% 	_'U*:*:!55nF]^^N 	[ * Utz UTX U!T!T!T!T^!T!T!T <<<<<<<<< U!T!T!T!T^!T!T!T \[[[[[[N % )@ " " " " "+" " " * [t/? [4> [!Z!Z!Z!Z^!Z!Z!Z; 	%n555ND)) **trJ   
componentslist[Calendar | Component]split_expandedc                .   g }|D ]}|                      |          }|rt          |          }|s-|rt          |          dk    r|D ]}t          |t                    rt                      }t          |t
                    rY|                                D ]
\  }	}
|
||	<   d |j        D             }|D ]&}ddlm	} |
                     ||                     'ddlm	} |
                     ||                     |                    |           ΐt          |t
                    rt                      }|                                D ]
\  }	}
|
||	<   d |j        D             }|D ]&}ddlm	} |
                     ||                     '|D ];}t          |t                    s$ddlm	} |
                     ||                     <|                    |           |D ]&}ddlm	} |                     ||                     '|S )a  Filter components according to the search criteria, optionally expanding recurrences.

        This method does not modify the input list. It returns a new list with
        filtered components.

        :param components: List of Calendar or Component objects to filter
        :param split_expanded: If True and recurrences are expanded, return each
            recurrence as a separate Calendar object. If False, all matching
            recurrences from a single input will be returned in one Calendar.
        :return: New list containing only components that match the filter criteria

        Examples:
            searcher = Searcher(event=True, start=datetime(2025, 1, 1))
            searcher.add_property_filter("SUMMARY", "meeting", operator="contains")
            filtered = searcher.filter(calendars)  # Returns matching calendars

            # With split_expanded=True, each recurrence becomes a separate Calendar
            searcher.expand = True
            split_results = searcher.filter(calendars, split_expanded=True)
        r   c                <    g | ]}t          |t                    |S  rA   r   r^   tzs     rH   rd   z#Searcher.filter.<locals>.<listcomp>  s9     ) ) )')
SUW_H`H`) ") ) )rJ   r   deepcopyc                <    g | ]}t          |t                    |S r   r   r   s     rH   rd   z#Searcher.filter.<locals>.<listcomp>'  s8     % % %#%JrS[D\D\%% % %rJ   )rz   r$   lenrA   r   r
   itemssubcomponentscopyr   add_componentrP   )rE   r   r   resultsrR   matchedmatched_listcompnew_calr0   r2   	timezonesr   r   s                 rH   filterzSearcher.filter  s   . /1# E	; E	;I**955G  @;#G}}# ! ;;c,&7&7!&;&; !- 0 0%dH55 %$ #+** &i:: D.7oo.?.? 5 5
U/4) )-6-D) ) )I '0 D D 9 9 9 9 9 9 ' 5 5hhrll C C C C 211111--hhtnn===w////308 ")X66 ;"****3//*;*; 1 1JC+0GCLL% %)2)@% % %	 #, @ @B555555#11((2,,???? %1 F FD#-dH#=#= F 9 9 9 9 9 9 ' 5 5hhtnn E E Ew//// %1 ; ;D555555#NN88D>>::::rJ   calendarr
   c                   ddl m} d |j        D             }d |j        D             }g }|D ]?}|                     |          }|r&t	          |          }|r|                    |           @|sdS t                      }	|                                D ]
\  }
}||	|
<   |D ] }|	                     ||                     !|D ]5}t          |t                    s|	                     ||                     6|	S )a  Filter subcomponents within a Calendar object according to search criteria.

        This method does not modify the input Calendar. It returns a new Calendar
        containing only the subcomponents that match the filter criteria.

        :param calendar: Calendar object containing multiple subcomponents to filter
        :return: New Calendar object with only matching subcomponents, or None if no matches

        Examples:
            searcher = Searcher(event=True, start=datetime(2025, 1, 1))
            searcher.add_property_filter("SUMMARY", "meeting", operator="contains")
            filtered_cal = searcher.filter_calendar(calendar)  # Returns Calendar with matching events
        r   r   c                <    g | ]}t          |t                    |S r   r   r^   r   s     rH   rd   z,Searcher.filter_calendar.<locals>.<listcomp>Q  s(    [[[d
4QY@Z@Z[T[[[rJ   c                <    g | ]}t          |t                    |S r   r   r   s     rH   rd   z,Searcher.filter_calendar.<locals>.<listcomp>R  s7     
 
 
:dH;U;U

 
 
rJ   N)r   r   r   rz   r$   extendr
   r   r   rA   r   )rE   r   r   r   other_componentsmatching_componentsr   r   r   new_calendarr0   r2   r   s                rH   filter_calendarzSearcher.filter_calendar@  sg    	"!!!!! \[h&<[[[	
 
%3
 
 

 !$ 	= 	=D**400G =#G}} ='..|<<< # 	4  zz #..** 	& 	&JC %L  	5 	5B&&xx||4444 ( 	; 	;DdH-- ;**88D>>:::rJ   (list[Component | CalendarObjectResource]c                d    | j         rt          || j                  S |                                S )a  Sort calendar objects according to configured sort keys.

        This method does not modify the input list. It returns a new sorted list.

        (the idea with this is that the input may be a generator)

        :param components: List of Calendar or CalendarObjectResource objects to sort
        :return: New sorted list

        Examples:
            searcher = Searcher()
            searcher.add_sort_key("DTSTART")
            sorted_events = searcher.sort(events)  # Returns new sorted list
        r0   )r%   sortedsorting_valuer   )rE   r   s     rH   sortzSearcher.sortv  s4    " ? 	%*$*<====??$$$rJ   c                   | j         sddlm}  ||          S ddlm fd|j        D             }fd|j        D             }t          || j                  }ddlm} t                      }|	                                D ]
\  }}|||<   |D ] }	|
                     ||	                     !|D ] }
|
                     ||
                     !|S )aH  Sort subcomponents within a Calendar object according to configured sort keys.

        This method does not modify the input Calendar. It returns a new Calendar
        with sorted subcomponents (excluding VTIMEZONE components).

        :param calendar: Calendar object containing multiple subcomponents to sort
        :return: New Calendar object with sorted subcomponents

        Examples:
            searcher = Searcher()
            searcher.add_sort_key("DTSTART")
            sorted_cal = searcher.sort_calendar(calendar)  # Returns new Calendar with sorted events
        r   r   )r   c                4    g | ]}t          |          |S r   rA   r^   r   r   s     rH   rd   z*Searcher.sort_calendar.<locals>.<listcomp>  s)    [[[d
4QY@Z@Z[T[[[rJ   c                4    g | ]}t          |          |S r   r   r   s     rH   rd   z*Searcher.sort_calendar.<locals>.<listcomp>  s8     
 
 
:dH;U;U

 
 
rJ   r   )r%   r   r   	icalendarr   r   r   r   r
   r   r   )rE   r   r   r   r   sorted_componentsr   r0   r2   r   r   r   s              @rH   sort_calendarzSearcher.sort_calendar  sU     	&%%%%%%8H%%% 	'&&&&&[[[[h&<[[[	
 
 
 
%3
 
 

 ##39KLLL 	"!!!!!zz #..** 	& 	&JC %L  	5 	5B&&xx||4444 & 	7 	7D&&xx~~6666rJ   "Component | CalendarObjectResourcetuplec           
        g }|                      |          }t          |t                    r!d |j        D             }t	          |          }n|}ddddddd|j                 d	d
|v oG|d
         j                            d          t          j	                                        d          k      d|v oG|d         j                            d          t          j	                                        d          k    d}| j
        D ]\  }}|dk    r|j        }n|                    |d          }|*|                    |                    |d	                     Vt          |t                    o|| j        v }	t!          |d          r|j        }t!          |d          r|                    d          }t          |t"                    r.t%          |          }d                    d |D                       }|	rst          |t                    r^| j        |         }
| j                            |          }| j                            |d          }t-          |
||          } ||          }|rbt          |t          t.          f          rCt          |t                    r|                                }t/          d |D                       }n| }|                    |           |S )zReturns a sortable value from the component, based on the sort keys

        The component may be an icalendar.Calendar, an
        icalendar.Component (i.e. icalendar.Event) or an
        caldav.CalendarObjectResource (i.e. caldav.Event).
        c              3  D   K   | ]}t          |t                    |V  d S rZ   r   r^   r_   s     rH   r`   z)Searcher.sorting_value.<locals>.<genexpr>  s3       c cq:VWYaKbKb c c c c c c crJ   z
2050-01-01z
1970-01-01r   zNEEDS-ACTIONFINAL	TENTATIVE)re   rg   rf    duez%F%H%M%Sdtstart)r   r   prioritystatusr=   rM   rN   r>   Ndtstrftime,c                ,    g | ]}t          |          S r   )r1   r   s     rH   rd   z*Searcher.sorting_value.<locals>.<listcomp>  s    4441A444rJ   Tc              3      K   | ]	}|d z  V  
dS )   Nr   )r^   bs     rH   r`   z)Searcher.sorting_value.<locals>.<genexpr>  s&      66QD666666rJ   )_unwraprA   r
   r   r   rl   r   r   r   nowr%   r>   getrP   r1   r'   hasattrr$   r   joinr(   r)   r   bytesencode)rE   rR   retnot_tz_componentsr   defaultssort_keyreversevalis_text_propertyr5   r7   r4   sort_key_fns                 rH   r   zSearcher.sorting_value  s    
 LL++	i** 	 c cI,C c c c)**DDD  #'#%  i	
   ^KN++J77(,..:Q:QR\:]:]]
 T! bO&//
;;hlnn>U>UV`>a>aa'
 
, "& 5	 5	Hg<''ohhx..{

8<<"55666  *#s33XDDX8XsD!! fsJ'' /ll:.. #t$$ 6 Skk hh4444455   'JsC$8$8 ' 0:	*..x88!%!:!>!>x!N!N3I~vVV!k#&& cC<00 !#s++ +!jjll66#66666CC$CJJsOOOO
rJ   !Calendar | CalendarObjectResourcec                    	 |j         }n# t          $ r Y nw xY wt          |t                    r:t          |t                    s%t	                      }|                    |           |}|S )z
        To support the caldav library (and possibly other libraries where the
        icalendar component is wrapped)
        )icalendar_instanceAttributeErrorrA   r   r
   r   )rE   rR   cals      rH   r   zSearcher._unwrap   s|    
	!4II 	 	 	D	i++ 	Jy(4S4S 	**Ci(((Is   
 
list[Component]c                   |                      |          }d |j        D             }t          |          st          d          |d         t          |          dk    red|d         vr
d|d         vsBt	          d |dd         D                       r!t          d	 |dd         D                       rt          d
          t          fd|D                       rt          d          |S )a8  This method serves two purposes:

        1) Be liberal in what "component" it accepts and return
        something well-defined.  For instance, coponent may be a
        wrapped object (caldav.Event), an icalendar.Calendar or an
        icalendar.Event.  The return value will always be a list of
        icalendar components (i.e. Event), and Timezone components will
        be removed.

        2) Do some verification that the "component" is as expected
        and raise a ValueError if not.  The "component" should either
        be one single component or a recurrence set.  A recurrence set
        should conform to those rules:

        2.1) All components in the recurrence set should have the same UID

        2.2) First element ("master") of the recurrence set may have the RRULE
        property set

        2.3) Any following elements of a recurrence set ("exception
        recurrences") should have the RECURRENCE-ID property set.

        2.4) (there are more properties that may only be set in the
        master or only in the recurrences, but currently we don't do
        more checking than this)

        As for now, we do not support component to be a generator or a
        list, and things will blow up if component.subcomponents is a
        generator yielding infinite or too many subcomponents.

        c                <    g | ]}t          |t                    |S r   r   r   s     rH   rd   z>Searcher._validate_and_normalize_component.<locals>.<listcomp>S  s'    XXXA
1h@W@WXaXXXrJ   zEmpty component?r   r   rX   zRECURRENCE-IDc              3     K   | ]}d |v V  	dS )zrecurrence-idNr   r   s     rH   r`   z=Searcher._validate_and_normalize_component.<locals>.<genexpr>`  s'      HHA?a/HHHHHHrJ   Nc              3     K   | ]}d |v V  	dS )rX   Nr   r   s     rH   r`   z=Searcher._validate_and_normalize_component.<locals>.<genexpr>a  s&      <<w!|<<<<<<rJ   z|Expected a valid recurrence set, either with one master component followed with special recurrences or with only occurrencesc              3  @   K   | ]}|d          d          k    |V  dS )uidNr   )r^   r_   r   s     rH   r`   z=Searcher._validate_and_normalize_component.<locals>.<genexpr>k  s5      AAQ%E%L(@(@q(@(@(@(@AArJ   zqInput parameter component is supposed to contain a single component or a recurrence set - but multiple UIDs found)r   r   r   
ValueErrorr~   r{   )rE   rR   r   r   s      @rH   rv   z*Searcher._validate_and_normalize_component/  s7   F LL++	XX!8XXX
 : 	1/0001
 z??Q
1--/TU2V2VHHABBHHHHH 3W<<Z^<<<<< 3W ! S   AAAA*AAAAA 	 D   rJ   r   rm   set[str]c                2   t                      }|D ]}|                    |           t          j        ||          }| j        r| j        nt          t                    }| j        r| j        nt          t                    }|	                    ||          S )a  Expand recurring events within the searcher's time range.

        Ensures expanded occurrences comply with RFC 5545:
        - Each occurrence has RECURRENCE-ID set
        - Each occurrence does NOT have RRULE (RRULE and RECURRENCE-ID are mutually exclusive)

        :param recurrence_set: List of calendar components to expand
        :param comptypesu: Set of component type strings (e.g., {"VEVENT", "VTODO"})
        :return: Iterable of expanded component instances
        )r   )
r
   r   recurring_ical_eventsofr   r   r   r   r   between)rE   r   rm   r   r_   recurr   r   s           rH   r}   zSearcher._expand_recurrencesq  s     jj 	! 	!Aa    %(DDD #jH

mK.H.H(Bdhhk(B(B}}UC(((rJ   )r/   TNN)r0   r1   r2   r	   r3   r1   r4   r   r5   r6   r7   r8   r9   r:   )NTNN)r0   r1   rK   r   r4   r   r5   r6   r7   r8   r9   r:   )FF)rR   rS   rT   r   rU   r   r9   rV   )F)r   r   r   r   r9   r   )r   r
   r9   r
   )r   r   r9   r   )rR   r   r9   r   )rR   r   r9   r
   )rR   rS   r9   r   )r   r   rm   r   r9   rV   )%__name__
__module____qualname____doc__r   __annotations__r   r   r   r   r   r    r!   r"   r   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   rI   rQ   rz   r   r   r   r   r   r   rv   r}   r   rJ   rH   r   r      s        X Xt DEGEC K    I"""""FuT222J2222!E$777O7777t444L4444!&t!<!<!<<<<<#eD9999999$uT::::::: %d ; ; ;;;;;"U48888888%*U4%@%@%@@@@@ ##&*!r@ r@ r@ r@ r@n #&*!4< 4< 4< 4< 4<r "',	j j j j jZ NS` ` ` ` `D4 4 4 4l% % % %,0 0 0 0d` ` ` `D   @ @ @ @D) ) ) ) ) )rJ   r   ) r   
__future__r   rw   collections.abcr   dataclassesr   r   r   typingr   r	   r   r   r
   r   r   r   r   r5   r   r   filtersr   utilsr   r   r   caldav.calendarobjectresourcer   r   r   rJ   rH   <module>r      si   H H " " " " " "  $ $ $ $ $ $ ( ( ( ( ( ( ( (       % % % % % % % %     3 3 3 3 3 3 3 3 3 3 : : : : : : : : 7 7 7 7 7 7 7 7             C C C C C C C C C C EDDDDDD o) o) o) o) o){ o) o) o) o) o)rJ   