<commit_before>%%%--------------------------------------------------------------------
%%% @author <NAME>
%%% @copyright (C) 2016 ACK CYFRONET AGH
%%% This software is released under the MIT license
%%% cited in 'LICENSE.txt'.
%%% @end
%%%--------------------------------------------------------------------
%%% @doc
%%% Opaque type storing informations about file.
%%% Once the record is created via new_* function and fslogic_worker has
%%% determined that the request can be handled locally - all of the functions
%%% in this module should work. If not - please report it.
%%% @end
%%%--------------------------------------------------------------------
-module(file_info).
-author("<NAME>").

-include("modules/datastore/datastore_specific_models_def.hrl").
-include("modules/fslogic/fslogic_common.hrl").
-include("proto/oneclient/fuse_messages.hrl").
-include_lib("ctool/include/posix/errors.hrl").
-include_lib("ctool/include/logging.hrl").

%% Record definition
-record(file_info, {
    cannonical_path :: undefined | path(),
    guid :: undefined | guid(),
    space_dir_doc :: undefined | file_meta:doc(),
    file_doc :: undefined | file_meta:doc() | {error, term()},
    parent :: undefined | file_info(),
    storage_file_id :: undefined | helpers:file(),
    space_name :: undefined | od_space:name() | od_space:alias(),
    storage_posix_user_context :: undefined | #posix_user_ctx{},
    times :: undefined | times:times(),
    file_name :: undefined | file_meta:name(),
    storage_doc :: undefined | space_storage:doc(),
    local_file_location_doc :: undefined | file_location:doc(),
    location_ids :: undefined | [file_location:id()]
}).

-type path() :: file_meta:path().
-type guid() :: fslogic_worker:file_guid().
-type file_info() :: #file_info{}.

%% API
-export([new_by_path/2, new_by_guid/1]).
-export([get_share_id/1, get_path/1, get_space_id/1, get_space_dir_uuid/1,
    get_guid/1, get_file_doc/1, get_parent/2, get_storage_file_id/1,
    get_aliased_name/2, get_storage_user_context/2, get_times/1, get_parent_guid/2,
    get_child/3, get_file_children/4, get_logical_path/2, get_uuid_entry/1, get_storage_doc/1, get_local_file_location_doc/1, get_file_location_ids/1]).
-export([is_file_info/1, is_space_dir/1, is_user_root_dir/2, is_root_dir/1, is_dir/1]).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Create new file_info using file's path
%% @end
%%--------------------------------------------------------------------
-spec new_by_path(fslogic_context:ctx(), path()) -> {NewCtx :: fslogic_context:ctx(), file_info()}.
new_by_path(Ctx, Path) ->
    {ok, Tokens} = fslogic_path:tokenize_skipping_dots(Path),
    case session:is_special(fslogic_context:get_session_id(Ctx)) of
        true ->
            throw({invalid_request, <<"Path resolution requested in the context
of special session. You may only operate on guids in this context.">>});
        false ->
            case Tokens of
                [<<"/">>] ->
                    {UserRootDirUuid, NewCtx} = fslogic_context:get_user_root_dir_uuid(Ctx),
                    UserRootDirGuid = fslogic_uuid:user_root_dir_guid(UserRootDirUuid),
                    {NewCtx, #file_info{cannonical_path = filename:join(Tokens), guid = UserRootDirGuid}};
                [<<"/">>, SpaceName | Rest] ->
                    {#document{value = #od_user{space_aliases = Spaces}}, Ctx2} =
                        fslogic_context:get_user(Ctx),

                    case lists:keyfind(SpaceName, 2, Spaces) of
                        false ->
                            throw(?ENOENT);
                        {SpaceId, SpaceName} ->
                            {Ctx2, #file_info{cannonical_path = filename:join([<<"/">>, SpaceId | Rest]), space_name = SpaceName}}
                    end
            end
    end.

%%--------------------------------------------------------------------
%% @doc
%% Create new file_info using file's guid
%% @end
%%--------------------------------------------------------------------
-spec new_by_guid(guid()) -> file_info().
new_by_guid(Guid) when Guid =/= undefined ->
    #file_info{guid = Guid}.

%%--------------------------------------------------------------------
%% @doc
%% Get file's share_id.
%% @end
%%--------------------------------------------------------------------
-spec get_share_id(fslogic_context:ctx()) -> od_share:id() | undefined.
get_share_id(#file_info{guid = undefined}) ->
    undefined;
get_share_id(#file_info{guid = Guid}) ->
    {_FileUuid, _SpaceId, ShareId} = fslogic_uuid:unpack_share_guid(Guid),
    ShareId;
get_share_id(Ctx) -> %todo TL return share_id cached in file_info
    fslogic_context:get_share_id(Ctx).

%%--------------------------------------------------------------------
%% @doc
%% Get file's cannonical path (starting with "/SpaceId/...."
%% @end
%%--------------------------------------------------------------------
-spec get_path(file_info()) -> {path(), file_info()}.
get_path(FileInfo = #file_info{cannonical_path = undefined}) ->
    case is_root_dir(FileInfo) of
        true ->
            {<<"/">>, FileInfo#file_info{cannonical_path = <<"/">>}};
        false ->
            {Guid, FileInfo2} = get_guid(FileInfo),
            Uuid = fslogic_uuid:guid_to_uuid(Guid),
            LogicalPath = fslogic_uuid:uuid_to_path(?ROOT_SESS_ID, Uuid),
            {ok, [<<"/">>, _SpaceName | Rest]} = fslogic_path:tokenize_skipping_dots(LogicalPath),
            SpaceId = get_space_id(FileInfo2),
            CanonicalPath = filename:join([<<"/">>, SpaceId | Rest]),
            {CanonicalPath, FileInfo2#file_info{cannonical_path = CanonicalPath}}
    end;
get_path(#file_info{cannonical_path = Path}) ->
    Path.

%%--------------------------------------------------------------------
%% @doc
%% Get file's logical path (starting with "/SpaceName/...."
%% @end
%%--------------------------------------------------------------------
-spec get_logical_path(file_info(), fslogic_context:ctx()) ->
    {file_meta:path(), fslogic_context:ctx(), file_info()}.
get_logical_path(FileInfo, Ctx) ->
    {Path, FileInfo2} = get_path(FileInfo),
    {SpaceName, Ctx2, FileInfo3} = get_space_name(FileInfo2, Ctx),
    {ok, [<<"/">>, _SpaceId | Rest]} = fslogic_path:tokenize_skipping_dots(Path),
    LogicalPath = filename:join([<<"/">>, SpaceName | Rest]),
    {LogicalPath, Ctx2, FileInfo3}.

%%--------------------------------------------------------------------
%% @doc
%% Get file's SpaceId
%% @end
%%--------------------------------------------------------------------
-spec get_space_id(file_info()) -> od_space:id().
get_space_id(#file_info{space_dir_doc = #document{key = SpaceUuid}}) ->
    fslogic_uuid:space_dir_uuid_to_spaceid(SpaceUuid);
get_space_id(#file_info{guid = undefined, cannonical_path = Path}) ->
    case fslogic_path:split(Path) of
        [<<"/">>, SpaceId | _] ->
            SpaceId;
        _ ->
            undefined
    end;
get_space_id(#file_info{guid = Guid}) ->
    fslogic_uuid:guid_to_space_id(Guid).

%%--------------------------------------------------------------------
%% @doc
%% Get file's SpaceDir uuid
%% @end
%%--------------------------------------------------------------------
-spec get_space_dir_uuid(file_info()) -> file_meta:uuid().
get_space_dir_uuid(#file_info{space_dir_doc = #document{key = SpaceUuid}}) ->
    SpaceUuid;
get_space_dir_uuid(FileInfo) ->
    fslogic_uuid:spaceid_to_space_dir_uuid(get_space_id(FileInfo)).<commit_msg>VFS-2856 Fix wrong logical path for root in file_info module.<commit_after>%%%--------------------------------------------------------------------
%%% @author <NAME>
%%% @copyright (C) 2016 ACK CYFRONET AGH
%%% This software is released under the MIT license
%%% cited in 'LICENSE.txt'.
%%% @end
%%%--------------------------------------------------------------------
%%% @doc
%%% Opaque type storing informations about file.
%%% Once the record is created via new_* function and fslogic_worker has
%%% determined that the request can be handled locally - all of the functions
%%% in this module should work. If not - please report it.
%%% @end
%%%--------------------------------------------------------------------
-module(file_info).
-author("<NAME>").

-include("modules/datastore/datastore_specific_models_def.hrl").
-include("modules/fslogic/fslogic_common.hrl").
-include("proto/oneclient/fuse_messages.hrl").
-include_lib("ctool/include/posix/errors.hrl").
-include_lib("ctool/include/logging.hrl").

%% Record definition
-record(file_info, {
    cannonical_path :: undefined | path(),
    guid :: undefined | guid(),
    space_dir_doc :: undefined | file_meta:doc(),
    file_doc :: undefined | file_meta:doc() | {error, term()},
    parent :: undefined | file_info(),
    storage_file_id :: undefined | helpers:file(),
    space_name :: undefined | od_space:name() | od_space:alias(),
    storage_posix_user_context :: undefined | #posix_user_ctx{},
    times :: undefined | times:times(),
    file_name :: undefined | file_meta:name(),
    storage_doc :: undefined | space_storage:doc(),
    local_file_location_doc :: undefined | file_location:doc(),
    location_ids :: undefined | [file_location:id()]
}).

-type path() :: file_meta:path().
-type guid() :: fslogic_worker:file_guid().
-type file_info() :: #file_info{}.

%% API
-export([new_by_path/2, new_by_guid/1]).
-export([get_share_id/1, get_path/1, get_space_id/1, get_space_dir_uuid/1,
    get_guid/1, get_file_doc/1, get_parent/2, get_storage_file_id/1,
    get_aliased_name/2, get_storage_user_context/2, get_times/1, get_parent_guid/2,
    get_child/3, get_file_children/4, get_logical_path/2, get_uuid_entry/1, get_storage_doc/1, get_local_file_location_doc/1, get_file_location_ids/1]).
-export([is_file_info/1, is_space_dir/1, is_user_root_dir/2, is_root_dir/1, is_dir/1]).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Create new file_info using file's path
%% @end
%%--------------------------------------------------------------------
-spec new_by_path(fslogic_context:ctx(), path()) -> {NewCtx :: fslogic_context:ctx(), file_info()}.
new_by_path(Ctx, Path) ->
    {ok, Tokens} = fslogic_path:tokenize_skipping_dots(Path),
    case session:is_special(fslogic_context:get_session_id(Ctx)) of
        true ->
            throw({invalid_request, <<"Path resolution requested in the context
of special session. You may only operate on guids in this context.">>});
        false ->
            case Tokens of
                [<<"/">>] ->
                    {UserRootDirUuid, NewCtx} = fslogic_context:get_user_root_dir_uuid(Ctx),
                    UserRootDirGuid = fslogic_uuid:user_root_dir_guid(UserRootDirUuid),
                    {NewCtx, #file_info{cannonical_path = filename:join(Tokens), guid = UserRootDirGuid}};
                [<<"/">>, SpaceName | Rest] ->
                    {#document{value = #od_user{space_aliases = Spaces}}, Ctx2} =
                        fslogic_context:get_user(Ctx),

                    case lists:keyfind(SpaceName, 2, Spaces) of
                        false ->
                            throw(?ENOENT);
                        {SpaceId, SpaceName} ->
                            {Ctx2, #file_info{cannonical_path = filename:join([<<"/">>, SpaceId | Rest]), space_name = SpaceName}}
                    end
            end
    end.

%%--------------------------------------------------------------------
%% @doc
%% Create new file_info using file's guid
%% @end
%%--------------------------------------------------------------------
-spec new_by_guid(guid()) -> file_info().
new_by_guid(Guid) when Guid =/= undefined ->
    #file_info{guid = Guid}.

%%--------------------------------------------------------------------
%% @doc
%% Get file's share_id.
%% @end
%%--------------------------------------------------------------------
-spec get_share_id(fslogic_context:ctx()) -> od_share:id() | undefined.
get_share_id(#file_info{guid = undefined}) ->
    undefined;
get_share_id(#file_info{guid = Guid}) ->
    {_FileUuid, _SpaceId, ShareId} = fslogic_uuid:unpack_share_guid(Guid),
    ShareId;
get_share_id(Ctx) -> %todo TL return share_id cached in file_info
    fslogic_context:get_share_id(Ctx).

%%--------------------------------------------------------------------
%% @doc
%% Get file's cannonical path (starting with "/SpaceId/...."
%% @end
%%--------------------------------------------------------------------
-spec get_path(file_info()) -> {path(), file_info()}.
get_path(FileInfo = #file_info{cannonical_path = undefined}) ->
    case is_root_dir(FileInfo) of
        true ->
            {<<"/">>, FileInfo#file_info{cannonical_path = <<"/">>}};
        false ->
            {Guid, FileInfo2} = get_guid(FileInfo),
            Uuid = fslogic_uuid:guid_to_uuid(Guid),
            LogicalPath = fslogic_uuid:uuid_to_path(?ROOT_SESS_ID, Uuid),
            {ok, [<<"/">>, _SpaceName | Rest]} = fslogic_path:tokenize_skipping_dots(LogicalPath),
            SpaceId = get_space_id(FileInfo2),
            CanonicalPath = filename:join([<<"/">>, SpaceId | Rest]),
            {CanonicalPath, FileInfo2#file_info{cannonical_path = CanonicalPath}}
    end;
get_path(#file_info{cannonical_path = Path}) ->
    Path.

%%--------------------------------------------------------------------
%% @doc
%% Get file's logical path (starting with "/SpaceName/...."
%% @end
%%--------------------------------------------------------------------
-spec get_logical_path(file_info(), fslogic_context:ctx()) ->
    {file_meta:path(), fslogic_context:ctx(), file_info()}.
get_logical_path(FileInfo, Ctx) ->
    case get_path(FileInfo) of
        {<<"/">>, FileInfo2} ->
            {<<"/">>, Ctx, FileInfo2};
        {Path, FileInfo2} ->
            {SpaceName, Ctx2, FileInfo3} = get_space_name(FileInfo2, Ctx),
            {ok, [<<"/">>, _SpaceId | Rest]} = fslogic_path:tokenize_skipping_dots(Path),
            LogicalPath = filename:join([<<"/">>, SpaceName | Rest]),
            {LogicalPath, Ctx2, FileInfo3}
    end.

%%--------------------------------------------------------------------
%% @doc
%% Get file's SpaceId
%% @end
%%--------------------------------------------------------------------
-spec get_space_id(file_info()) -> od_space:id().
get_space_id(#file_info{space_dir_doc = #document{key = SpaceUuid}}) ->
    fslogic_uuid:space_dir_uuid_to_spaceid(SpaceUuid);
get_space_id(#file_info{guid = undefined, cannonical_path = Path}) ->
    case fslogic_path:split(Path) of
        [<<"/">>, SpaceId | _] ->
            SpaceId;
        _ ->
            undefined
    end;
get_space_id(#file_info{guid = Guid}) ->
    fslogic_uuid:guid_to_space_id(Guid).

%%--------------------------------------------------------------------
%% @doc
%% Get file's SpaceDir uuid
%% @end
%%--------------------------------------------------------------------
-spec get_space_dir_uuid(file_info()) -> file_meta:uuid().
get_space_dir_uuid(#file_info{space_dir_doc = #document{key = SpaceUuid}}) ->
    SpaceUuid;
get_space_dir_uuid(FileInfo) ->
    fslogic_uuid:spaceid_to_space_dir_uuid(get_space_id(FileInfo)).
{'fraction_non_alphanumeric': 0.29781721614094175, 'fraction_numerical': 0.005829393095407733, 'mean_word_length': 1.9905093937633158, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4219165', 'n_tokens_mistral': 4610, 'n_tokens_neox': 4453, 'n_words': 818}