115 lines
2.7 KiB
Python
115 lines
2.7 KiB
Python
import re
|
|
from dataclasses import dataclass
|
|
from typing import Iterator, Protocol
|
|
|
|
from fastapi import HTTPException, status
|
|
from webdav3.exceptions import RemoteResourceNotFound
|
|
|
|
from ..dav_common import caldav_principal, webdav_list
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NameLister(Protocol):
|
|
def __call__(self) -> Iterator[str]:
|
|
...
|
|
|
|
|
|
_RESPONSE_OK = {
|
|
status.HTTP_200_OK: {
|
|
"description": "Operation successful",
|
|
},
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FileNameLister:
|
|
remote_path: str
|
|
re: re.Pattern[str]
|
|
|
|
@property
|
|
def responses(self) -> dict:
|
|
return {
|
|
**_RESPONSE_OK,
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"description": f"{self.remote_path!r} not found",
|
|
"content": None,
|
|
},
|
|
}
|
|
|
|
async def __call__(self) -> Iterator[str]:
|
|
try:
|
|
file_names = await webdav_list(self.remote_path)
|
|
|
|
return (
|
|
name
|
|
for name in file_names
|
|
if self.re.search(name)
|
|
)
|
|
|
|
except RemoteResourceNotFound:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CalendarNameLister:
|
|
async def __call__(self) -> Iterator[str]:
|
|
return (
|
|
cal.name
|
|
for cal in caldav_principal().calendars()
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PrefixFinder:
|
|
lister: NameLister
|
|
|
|
@property
|
|
def responses(self) -> dict:
|
|
return {
|
|
**_RESPONSE_OK,
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"description": "Failure in lister " +
|
|
repr(self.lister.__class__.__name__),
|
|
"content": None,
|
|
},
|
|
}
|
|
|
|
async def __call__(self, prefix: str) -> Iterator[str]:
|
|
return (
|
|
file_name
|
|
for file_name in (await self.lister())
|
|
if file_name.lower().startswith(prefix.lower())
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PrefixUnique:
|
|
finder: PrefixFinder
|
|
|
|
@property
|
|
def responses(self) -> dict:
|
|
return {
|
|
**_RESPONSE_OK,
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"description": "Prefix not found",
|
|
"content": None,
|
|
},
|
|
status.HTTP_409_CONFLICT: {
|
|
"description": "Ambiguous prefix",
|
|
"content": None,
|
|
},
|
|
}
|
|
|
|
async def __call__(self, prefix: str) -> str:
|
|
names = await self.finder(prefix)
|
|
|
|
try:
|
|
name = next(names)
|
|
|
|
except StopIteration:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
|
|
if any(True for _ in names):
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
|
|
|
|
return name
|